<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.sojielectronics.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Admin</id>
	<title>SOJI ELECTRONICS - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.sojielectronics.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Admin"/>
	<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/wiki/Special:Contributions/Admin"/>
	<updated>2026-11-10T02:23:10Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.45.4</generator>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=File:CalibTable-Generator-Icon.png&amp;diff=2127</id>
		<title>File:CalibTable-Generator-Icon.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=File:CalibTable-Generator-Icon.png&amp;diff=2127"/>
		<updated>2026-11-09T07:01:41Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=CalibTable_Generator&amp;diff=2126</id>
		<title>CalibTable Generator</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=CalibTable_Generator&amp;diff=2126"/>
		<updated>2026-11-09T06:56:44Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Support / Contact */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
This feature generates a complete fuel tank calibration table from just &#039;&#039;&#039;three refuels&#039;&#039;&#039;. The user only needs to select the tank shape, enter the tank height and the sensor height after cutting, and the fuel level and litres of each refuel; the app calculates all points of the table automatically, with no need to fill in small increments and manually record each point. Available from app version 2.2.4.&lt;br /&gt;
&lt;br /&gt;
The table generator has two modes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mode !! Description !! Sensor connection !! Output&lt;br /&gt;
|-&lt;br /&gt;
| Standalone || Refuel data is already available, or preparing a table for LIGO AIR, LIGO MAX, LIGO PRO BLE || Not required || CSV file&lt;br /&gt;
|-&lt;br /&gt;
| Sensor Live || Calibrate directly on the vehicle while connected to a LIGO AIR or LIGO PRO BLE / PRO 2(S) sensor || Required || Written directly to the sensor or to a CSV file&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Supported tank shapes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;200&amp;quot; heights=&amp;quot;150&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-tank-parallelepiped.png|&#039;&#039;Parallelepiped&#039;&#039;&lt;br /&gt;
File:AutoCalib-tank-cylinder.png|&#039;&#039;Cylinder&#039;&#039;&lt;br /&gt;
File:AutoCalib-tank-dshape.png|&#039;&#039;D-shape&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparation ==&lt;br /&gt;
&lt;br /&gt;
* The fuel tank is empty or fully drained.&lt;br /&gt;
* The sensor has been cut to the correct length and installed on the tank.&lt;br /&gt;
* A tool to accurately measure the litres added is available (a metered pump or a calibrated measuring container).&lt;br /&gt;
* The two tank dimensions are known:&lt;br /&gt;
** &#039;&#039;&#039;H – Tank height (cm):&#039;&#039;&#039; the internal height of the tank at the sensor mounting position, measured from the bottom.&lt;br /&gt;
** &#039;&#039;&#039;L – Sensor height (cm):&#039;&#039;&#039; the length of the sensor after cutting, measured from the lower edge of the aluminium tube to the underside of the sensor base.&lt;br /&gt;
&lt;br /&gt;
The difference &#039;&#039;&#039;d = H − L&#039;&#039;&#039; is the bottom gap; fuel below the probe tip is not measured by the sensor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Three-refuel plan:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Refuel 1 (offset):&#039;&#039;&#039; starting from an empty tank, fill to about 5–10 % of the tank volume.&lt;br /&gt;
# &#039;&#039;&#039;Refuel 2:&#039;&#039;&#039; fill to about 40–60 %.&lt;br /&gt;
# &#039;&#039;&#039;Refuel 3:&#039;&#039;&#039; fill the tank completely (100 %).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; For each refuel, record only the litres added in that refuel; &#039;&#039;&#039;do not accumulate&#039;&#039;&#039; the litres from previous refuels.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;[[File:AutoCalib-dim-cylinder.png|frameless|400px]]&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Tank height H, sensor height L and bottom gap d&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Opening the Calibration Table Generator ==&lt;br /&gt;
&lt;br /&gt;
=== Standalone Mode ===&lt;br /&gt;
&lt;br /&gt;
# Open the app; on the &#039;&#039;&#039;Bluetooth Products&#039;&#039;&#039; page, select &#039;&#039;&#039;CALIBTABLE GENERATOR&#039;&#039;&#039;.&lt;br /&gt;
# The &#039;&#039;&#039;CalibTable Generator&#039;&#039;&#039; page opens; no sensor connection is required.&lt;br /&gt;
&lt;br /&gt;
=== Sensor Live Mode ===&lt;br /&gt;
&lt;br /&gt;
# Connect the LIGO AIR or LIGO PRO BLE sensor as usual.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Settings&#039;&#039;&#039; tab &amp;gt; &#039;&#039;&#039;Calibration Table&#039;&#039;&#039; &amp;gt; &#039;&#039;&#039;Auto Calibration&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page looks the same as in Standalone mode, with an additional &#039;&#039;&#039;Fuel level from sensor&#039;&#039;&#039; line showing the live reading from the sensor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &#039;&#039;&#039;Manual Calibration&#039;&#039;&#039; item in the same menu is used to build a multi-point calibration table manually; this document covers Auto Calibration only.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-01-products-generator.png|&#039;&#039;Figure 2. CALIBTABLE GENERATOR on the Bluetooth Products page&#039;&#039;&lt;br /&gt;
File:AutoCalib-02-settings-calibration-table.png|&#039;&#039;Figure 3. Settings &amp;gt; Calibration Table&#039;&#039;&lt;br /&gt;
File:AutoCalib-03-calibration-menu.png|&#039;&#039;Figure 4. Calibration Table menu&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Step 1 – Tank Type and Dimensions ==&lt;br /&gt;
&lt;br /&gt;
# Select the tank shape: &#039;&#039;&#039;Parallelepiped&#039;&#039;&#039;, &#039;&#039;&#039;Cylinder&#039;&#039;&#039; or &#039;&#039;&#039;D-shape&#039;&#039;&#039;.&lt;br /&gt;
# Enter &#039;&#039;&#039;Tank height (H)&#039;&#039;&#039; and &#039;&#039;&#039;Sensor height (L)&#039;&#039;&#039; in cm (decimals are accepted, e.g. 60.5).&lt;br /&gt;
# The app automatically calculates &#039;&#039;&#039;Bottom gap: d = H − L&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* L must not be greater than H.&lt;br /&gt;
* Tap the ⓘ icon next to each field to view a short explanation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;190&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-dim-parallelepiped.png|&#039;&#039;Measuring H, L – rectangular box&#039;&#039;&lt;br /&gt;
File:AutoCalib-dim-cylinder.png|&#039;&#039;Measuring H, L – horizontal cylinder&#039;&#039;&lt;br /&gt;
File:AutoCalib-dim-dshape.png|&#039;&#039;Measuring H, L – D-shape&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Step 2 – Refuel Calibration ==&lt;br /&gt;
&lt;br /&gt;
The refuel table has three rows, one per refuel (row 1 starts from an empty tank). Target fill levels for each tank shape:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Refuel 1 (offset): 5–10 % !! Refuel 2: 40–60 % !! Refuel 3: full (100 %)&lt;br /&gt;
|-&lt;br /&gt;
| [[File:AutoCalib-refuel-parallelepiped-1.png|frameless|180px]] || [[File:AutoCalib-refuel-parallelepiped-2.png|frameless|180px]] || [[File:AutoCalib-refuel-parallelepiped-3.png|frameless|180px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Refuel table columns:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Fuel level start || Taken automatically from the Fuel level end of the previous row; the first row is always 0.&lt;br /&gt;
|-&lt;br /&gt;
| Fuel level end || Fuel level reported by the sensor after the refuel, in sensor units (0 – 4095).&lt;br /&gt;
|-&lt;br /&gt;
| ΔV (L) || Litres added in this refuel (not cumulative).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For each refuel:&lt;br /&gt;
&lt;br /&gt;
=== In Standalone Mode (the user already has the refuel data) ===&lt;br /&gt;
&lt;br /&gt;
Enter the existing data of the 3 refuels into the 3 corresponding rows &#039;&#039;&#039;1st refuel&#039;&#039;&#039;, &#039;&#039;&#039;2nd refuel&#039;&#039;&#039;, &#039;&#039;&#039;3rd refuel&#039;&#039;&#039; (including the Fuel level end and the corresponding litres of that refuel).&lt;br /&gt;
&lt;br /&gt;
=== Reading the Fuel Level in Sensor Live Mode ===&lt;br /&gt;
&lt;br /&gt;
# Fill to the target level for that row and wait for the fuel surface to settle.&lt;br /&gt;
# Read the fuel level from the sensor and enter it in &#039;&#039;&#039;Fuel level end&#039;&#039;&#039;.&lt;br /&gt;
# Enter the litres added in &#039;&#039;&#039;ΔV (L)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* Tap ⓘ next to the row name to view the target fill level illustration for the selected tank shape.&lt;br /&gt;
* When you enter &#039;&#039;&#039;Fuel level end&#039;&#039;&#039;, the tank illustration in Step 1 shows an estimated % mark, helping you check whether the value is reasonable.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Fuel level from sensor&#039;&#039;&#039; line above the refuel table shows the live reading with its status:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Stable&#039;&#039;&#039; (green): enter this value in the &#039;&#039;&#039;Fuel level end&#039;&#039;&#039; of the current refuel.&lt;br /&gt;
* &#039;&#039;&#039;Unstable&#039;&#039;&#039; (red): the fuel surface is still moving – wait longer before recording.&lt;br /&gt;
&lt;br /&gt;
=== Rows Highlighted in Red ===&lt;br /&gt;
&lt;br /&gt;
A refuel row turns red when &#039;&#039;&#039;Fuel level end&#039;&#039;&#039; ≤ &#039;&#039;&#039;Fuel level start&#039;&#039;&#039;, &#039;&#039;&#039;Fuel level end&#039;&#039;&#039; &amp;amp;gt; 4095, or &#039;&#039;&#039;ΔV&#039;&#039;&#039; ≤ 0. Correct the values in that row before generating the table.&lt;br /&gt;
&lt;br /&gt;
== Step 3 – Export the Table ==&lt;br /&gt;
&lt;br /&gt;
# Select &#039;&#039;&#039;Sensor type&#039;&#039;&#039;; the number of calibration points is set automatically according to the sensor.&lt;br /&gt;
# To use a different number of points: select &#039;&#039;&#039;Custom&#039;&#039;&#039; and enter it in &#039;&#039;&#039;Calibration points (custom)&#039;&#039;&#039;; the points are distributed evenly over the 0 – 4095 range.&lt;br /&gt;
# Tap &#039;&#039;&#039;Generate calibration table&#039;&#039;&#039;. If the data is invalid, a red message box appears above the button (see the Error Messages section).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mode !! Sensor type options&lt;br /&gt;
|-&lt;br /&gt;
| Standalone || LIGO AIR (50 points), LIGO PRO BLE (30 points), LIGO MAX (100 points), Custom (2 – 200 points)&lt;br /&gt;
|-&lt;br /&gt;
| Sensor Live – LIGO PRO BLE || LIGO PRO BLE (30 points), Custom (2 – 30 points)&lt;br /&gt;
|-&lt;br /&gt;
| Sensor Live – LIGO AIR || LIGO AIR (50 points), Custom (2 – 50 points)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-04-step1.png|&#039;&#039;Figure 5. Step 1 – tank type and dimensions&#039;&#039;&lt;br /&gt;
File:AutoCalib-05-step2.png|&#039;&#039;Figure 6. Step 2 – refuel calibration&#039;&#039;&lt;br /&gt;
File:AutoCalib-06-step2-live.png|&#039;&#039;Figure 7. Fuel level from sensor (Sensor Live)&#039;&#039;&lt;br /&gt;
File:AutoCalib-07-step3.png|&#039;&#039;Figure 8. Step 3 – export settings&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Results ==&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Results&#039;&#039;&#039; page shows:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Item !! Description !! Item !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Tank shape || Selected tank shape || V(4095) || Volume at the maximum sensor level – the largest measurable volume (L)&lt;br /&gt;
|-&lt;br /&gt;
| Parameters || H, L and d || V(0) || Volume at level 0&lt;br /&gt;
|-&lt;br /&gt;
| Calibration mode || Number of refuels used || Points || Number of points in the table&lt;br /&gt;
|-&lt;br /&gt;
| Offset || Fuel level and litres of refuel 1 || ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The calibration table lists each point with the columns &#039;&#039;&#039;#, Fuel level, V (L)&#039;&#039;&#039; and &#039;&#039;&#039;Zone&#039;&#039;&#039; (zero, offset, refuel 2, refuel 3, full). Tap &#039;&#039;&#039;Chart&#039;&#039;&#039; (top-right corner) to view the volume-versus-fuel-level graph – the curve should rise smoothly, with no sudden jumps.&lt;br /&gt;
&lt;br /&gt;
=== Download CSV File (Both Modes) ===&lt;br /&gt;
&lt;br /&gt;
Tap &#039;&#039;&#039;Download CSV&#039;&#039;&#039; and choose a save location on the phone; the app shows &#039;&#039;&#039;Success&#039;&#039;&#039; with the file path. Use the file to load the table into LIGO MAX, keep installation records, or import it via Manual Calibration.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Item !! Value&lt;br /&gt;
|-&lt;br /&gt;
| File name || yyyyMMdd_HHmmss_&amp;amp;lt;points&amp;amp;gt;points_&amp;amp;lt;tank&amp;amp;gt;_H&amp;amp;lt;H&amp;amp;gt;_L&amp;amp;lt;L&amp;amp;gt;.csv, e.g. 20261103_101530_50points_cylinder_H60.5_L57.5.csv&lt;br /&gt;
|-&lt;br /&gt;
| Columns || No, Volume (liter), Level&lt;br /&gt;
|-&lt;br /&gt;
| Limits || Maximum 200 points; each volume value up to 65,535 L&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Save to Sensor (Sensor Live Mode Only) ===&lt;br /&gt;
&lt;br /&gt;
Tap &#039;&#039;&#039;Save to sensor&#039;&#039;&#039;, select &#039;&#039;&#039;Yes&#039;&#039;&#039; in the &amp;quot;Would you like to save calibration points?&amp;quot; dialog and wait until &amp;quot;Calibration points saved successfully!&amp;quot; appears. For LIGO AIR, the app automatically enables the calibration table on the sensor after writing; no further setup is needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-09-results.png|&#039;&#039;Figure 9. Results page&#039;&#039;&lt;br /&gt;
File:AutoCalib-10-chart.png|&#039;&#039;Figure 10. Calibration chart&#039;&#039;&lt;br /&gt;
File:AutoCalib-11-csv-saved.png|&#039;&#039;Figure 11. CSV file saved&#039;&#039;&lt;br /&gt;
File:AutoCalib-12-save-sensor.png|&#039;&#039;Figure 12. Table saved to the sensor&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Error Messages ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;[[File:AutoCalib-08-error.png|frameless|300px]]&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 13. Example of an input error message&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Message !! Cause !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| Tank height H is empty. / Sensor height is empty. || H or L has not been entered || Enter both values in Step 1&lt;br /&gt;
|-&lt;br /&gt;
| Sensor height (…) cannot exceed tank height (…) || L is greater than H || Re-measure H and L&lt;br /&gt;
|-&lt;br /&gt;
| 1st refuel (offset): fuel level end is empty. (or litres added) || The refuel row is incomplete || Fill in all three rows in Step 2&lt;br /&gt;
|-&lt;br /&gt;
| Fuel level offset must be within 1…4093. || Fuel level end of refuel 1 is 0 or too high || Check the reading after refuel 1&lt;br /&gt;
|-&lt;br /&gt;
| Refuel N: fuel level end = X must be greater than fuel level start = Y. || The level after refuel N is not higher than before || Check the reading of refuel N&lt;br /&gt;
|-&lt;br /&gt;
| Refuel N: fuel level end cannot exceed 4095. || The value exceeds the sensor&#039;s measuring range || Enter a value from 0 to 4095&lt;br /&gt;
|-&lt;br /&gt;
| Voffset must be greater than 0 / Refuel N: ΔV must be greater than 0. || Litres added is 0 or negative || Enter the correct litres added&lt;br /&gt;
|-&lt;br /&gt;
| Number of points must be within 2…&amp;amp;lt;max&amp;amp;gt;. || Custom number of points is out of range || Enter a value within the range in Step 3&lt;br /&gt;
|-&lt;br /&gt;
| Total fuel volumn (…) cannot exeed 65535. || Total litres too large || Re-check the litres entered&lt;br /&gt;
|-&lt;br /&gt;
| Cannot export – LIGO MAX accepts at most 200 calibration points. || The table has more than 200 points || Regenerate the table with at most 200 points&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Software &amp;amp; Applications]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=Category:CalibTable_Generator&amp;diff=2125</id>
		<title>Category:CalibTable Generator</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=Category:CalibTable_Generator&amp;diff=2125"/>
		<updated>2026-11-09T06:55:41Z</updated>

		<summary type="html">&lt;p&gt;Admin: Auto-created category for CalibTable Generator&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- auto-created category --&amp;gt;&lt;br /&gt;
{{DEFAULTSORT:05 CalibTable Generator}}&lt;br /&gt;
&lt;br /&gt;
[[Category:Software &amp;amp; Applications]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=CalibTable_Generator&amp;diff=2124</id>
		<title>CalibTable Generator</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=CalibTable_Generator&amp;diff=2124"/>
		<updated>2026-11-09T06:55:41Z</updated>

		<summary type="html">&lt;p&gt;Admin: Initial Release [release]&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
This feature generates a complete fuel tank calibration table from just &#039;&#039;&#039;three refuels&#039;&#039;&#039;. The user only needs to select the tank shape, enter the tank height and the sensor height after cutting, and the fuel level and litres of each refuel; the app calculates all points of the table automatically, with no need to fill in small increments and manually record each point. Available from app version 2.2.4.&lt;br /&gt;
&lt;br /&gt;
The table generator has two modes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mode !! Description !! Sensor connection !! Output&lt;br /&gt;
|-&lt;br /&gt;
| Standalone || Refuel data is already available, or preparing a table for LIGO AIR, LIGO MAX, LIGO PRO BLE || Not required || CSV file&lt;br /&gt;
|-&lt;br /&gt;
| Sensor Live || Calibrate directly on the vehicle while connected to a LIGO AIR or LIGO PRO BLE / PRO 2(S) sensor || Required || Written directly to the sensor or to a CSV file&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Supported tank shapes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;200&amp;quot; heights=&amp;quot;150&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-tank-parallelepiped.png|&#039;&#039;Parallelepiped&#039;&#039;&lt;br /&gt;
File:AutoCalib-tank-cylinder.png|&#039;&#039;Cylinder&#039;&#039;&lt;br /&gt;
File:AutoCalib-tank-dshape.png|&#039;&#039;D-shape&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Preparation ==&lt;br /&gt;
&lt;br /&gt;
* The fuel tank is empty or fully drained.&lt;br /&gt;
* The sensor has been cut to the correct length and installed on the tank.&lt;br /&gt;
* A tool to accurately measure the litres added is available (a metered pump or a calibrated measuring container).&lt;br /&gt;
* The two tank dimensions are known:&lt;br /&gt;
** &#039;&#039;&#039;H – Tank height (cm):&#039;&#039;&#039; the internal height of the tank at the sensor mounting position, measured from the bottom.&lt;br /&gt;
** &#039;&#039;&#039;L – Sensor height (cm):&#039;&#039;&#039; the length of the sensor after cutting, measured from the lower edge of the aluminium tube to the underside of the sensor base.&lt;br /&gt;
&lt;br /&gt;
The difference &#039;&#039;&#039;d = H − L&#039;&#039;&#039; is the bottom gap; fuel below the probe tip is not measured by the sensor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Three-refuel plan:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Refuel 1 (offset):&#039;&#039;&#039; starting from an empty tank, fill to about 5–10 % of the tank volume.&lt;br /&gt;
# &#039;&#039;&#039;Refuel 2:&#039;&#039;&#039; fill to about 40–60 %.&lt;br /&gt;
# &#039;&#039;&#039;Refuel 3:&#039;&#039;&#039; fill the tank completely (100 %).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; For each refuel, record only the litres added in that refuel; &#039;&#039;&#039;do not accumulate&#039;&#039;&#039; the litres from previous refuels.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;[[File:AutoCalib-dim-cylinder.png|frameless|400px]]&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Tank height H, sensor height L and bottom gap d&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Opening the Calibration Table Generator ==&lt;br /&gt;
&lt;br /&gt;
=== Standalone Mode ===&lt;br /&gt;
&lt;br /&gt;
# Open the app; on the &#039;&#039;&#039;Bluetooth Products&#039;&#039;&#039; page, select &#039;&#039;&#039;CALIBTABLE GENERATOR&#039;&#039;&#039;.&lt;br /&gt;
# The &#039;&#039;&#039;CalibTable Generator&#039;&#039;&#039; page opens; no sensor connection is required.&lt;br /&gt;
&lt;br /&gt;
=== Sensor Live Mode ===&lt;br /&gt;
&lt;br /&gt;
# Connect the LIGO AIR or LIGO PRO BLE sensor as usual.&lt;br /&gt;
# Go to the &#039;&#039;&#039;Settings&#039;&#039;&#039; tab &amp;gt; &#039;&#039;&#039;Calibration Table&#039;&#039;&#039; &amp;gt; &#039;&#039;&#039;Auto Calibration&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The page looks the same as in Standalone mode, with an additional &#039;&#039;&#039;Fuel level from sensor&#039;&#039;&#039; line showing the live reading from the sensor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The &#039;&#039;&#039;Manual Calibration&#039;&#039;&#039; item in the same menu is used to build a multi-point calibration table manually; this document covers Auto Calibration only.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-01-products-generator.png|&#039;&#039;Figure 2. CALIBTABLE GENERATOR on the Bluetooth Products page&#039;&#039;&lt;br /&gt;
File:AutoCalib-02-settings-calibration-table.png|&#039;&#039;Figure 3. Settings &amp;gt; Calibration Table&#039;&#039;&lt;br /&gt;
File:AutoCalib-03-calibration-menu.png|&#039;&#039;Figure 4. Calibration Table menu&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Step 1 – Tank Type and Dimensions ==&lt;br /&gt;
&lt;br /&gt;
# Select the tank shape: &#039;&#039;&#039;Parallelepiped&#039;&#039;&#039;, &#039;&#039;&#039;Cylinder&#039;&#039;&#039; or &#039;&#039;&#039;D-shape&#039;&#039;&#039;.&lt;br /&gt;
# Enter &#039;&#039;&#039;Tank height (H)&#039;&#039;&#039; and &#039;&#039;&#039;Sensor height (L)&#039;&#039;&#039; in cm (decimals are accepted, e.g. 60.5).&lt;br /&gt;
# The app automatically calculates &#039;&#039;&#039;Bottom gap: d = H − L&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* L must not be greater than H.&lt;br /&gt;
* Tap the ⓘ icon next to each field to view a short explanation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;190&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-dim-parallelepiped.png|&#039;&#039;Measuring H, L – rectangular box&#039;&#039;&lt;br /&gt;
File:AutoCalib-dim-cylinder.png|&#039;&#039;Measuring H, L – horizontal cylinder&#039;&#039;&lt;br /&gt;
File:AutoCalib-dim-dshape.png|&#039;&#039;Measuring H, L – D-shape&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Step 2 – Refuel Calibration ==&lt;br /&gt;
&lt;br /&gt;
The refuel table has three rows, one per refuel (row 1 starts from an empty tank). Target fill levels for each tank shape:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Refuel 1 (offset): 5–10 % !! Refuel 2: 40–60 % !! Refuel 3: full (100 %)&lt;br /&gt;
|-&lt;br /&gt;
| [[File:AutoCalib-refuel-parallelepiped-1.png|frameless|180px]] || [[File:AutoCalib-refuel-parallelepiped-2.png|frameless|180px]] || [[File:AutoCalib-refuel-parallelepiped-3.png|frameless|180px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Refuel table columns:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Fuel level start || Taken automatically from the Fuel level end of the previous row; the first row is always 0.&lt;br /&gt;
|-&lt;br /&gt;
| Fuel level end || Fuel level reported by the sensor after the refuel, in sensor units (0 – 4095).&lt;br /&gt;
|-&lt;br /&gt;
| ΔV (L) || Litres added in this refuel (not cumulative).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For each refuel:&lt;br /&gt;
&lt;br /&gt;
=== In Standalone Mode (the user already has the refuel data) ===&lt;br /&gt;
&lt;br /&gt;
Enter the existing data of the 3 refuels into the 3 corresponding rows &#039;&#039;&#039;1st refuel&#039;&#039;&#039;, &#039;&#039;&#039;2nd refuel&#039;&#039;&#039;, &#039;&#039;&#039;3rd refuel&#039;&#039;&#039; (including the Fuel level end and the corresponding litres of that refuel).&lt;br /&gt;
&lt;br /&gt;
=== Reading the Fuel Level in Sensor Live Mode ===&lt;br /&gt;
&lt;br /&gt;
# Fill to the target level for that row and wait for the fuel surface to settle.&lt;br /&gt;
# Read the fuel level from the sensor and enter it in &#039;&#039;&#039;Fuel level end&#039;&#039;&#039;.&lt;br /&gt;
# Enter the litres added in &#039;&#039;&#039;ΔV (L)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
* Tap ⓘ next to the row name to view the target fill level illustration for the selected tank shape.&lt;br /&gt;
* When you enter &#039;&#039;&#039;Fuel level end&#039;&#039;&#039;, the tank illustration in Step 1 shows an estimated % mark, helping you check whether the value is reasonable.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Fuel level from sensor&#039;&#039;&#039; line above the refuel table shows the live reading with its status:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Stable&#039;&#039;&#039; (green): enter this value in the &#039;&#039;&#039;Fuel level end&#039;&#039;&#039; of the current refuel.&lt;br /&gt;
* &#039;&#039;&#039;Unstable&#039;&#039;&#039; (red): the fuel surface is still moving – wait longer before recording.&lt;br /&gt;
&lt;br /&gt;
=== Rows Highlighted in Red ===&lt;br /&gt;
&lt;br /&gt;
A refuel row turns red when &#039;&#039;&#039;Fuel level end&#039;&#039;&#039; ≤ &#039;&#039;&#039;Fuel level start&#039;&#039;&#039;, &#039;&#039;&#039;Fuel level end&#039;&#039;&#039; &amp;amp;gt; 4095, or &#039;&#039;&#039;ΔV&#039;&#039;&#039; ≤ 0. Correct the values in that row before generating the table.&lt;br /&gt;
&lt;br /&gt;
== Step 3 – Export the Table ==&lt;br /&gt;
&lt;br /&gt;
# Select &#039;&#039;&#039;Sensor type&#039;&#039;&#039;; the number of calibration points is set automatically according to the sensor.&lt;br /&gt;
# To use a different number of points: select &#039;&#039;&#039;Custom&#039;&#039;&#039; and enter it in &#039;&#039;&#039;Calibration points (custom)&#039;&#039;&#039;; the points are distributed evenly over the 0 – 4095 range.&lt;br /&gt;
# Tap &#039;&#039;&#039;Generate calibration table&#039;&#039;&#039;. If the data is invalid, a red message box appears above the button (see the Error Messages section).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mode !! Sensor type options&lt;br /&gt;
|-&lt;br /&gt;
| Standalone || LIGO AIR (50 points), LIGO PRO BLE (30 points), LIGO MAX (100 points), Custom (2 – 200 points)&lt;br /&gt;
|-&lt;br /&gt;
| Sensor Live – LIGO PRO BLE || LIGO PRO BLE (30 points), Custom (2 – 30 points)&lt;br /&gt;
|-&lt;br /&gt;
| Sensor Live – LIGO AIR || LIGO AIR (50 points), Custom (2 – 50 points)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-04-step1.png|&#039;&#039;Figure 5. Step 1 – tank type and dimensions&#039;&#039;&lt;br /&gt;
File:AutoCalib-05-step2.png|&#039;&#039;Figure 6. Step 2 – refuel calibration&#039;&#039;&lt;br /&gt;
File:AutoCalib-06-step2-live.png|&#039;&#039;Figure 7. Fuel level from sensor (Sensor Live)&#039;&#039;&lt;br /&gt;
File:AutoCalib-07-step3.png|&#039;&#039;Figure 8. Step 3 – export settings&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Results ==&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Results&#039;&#039;&#039; page shows:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Item !! Description !! Item !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Tank shape || Selected tank shape || V(4095) || Volume at the maximum sensor level – the largest measurable volume (L)&lt;br /&gt;
|-&lt;br /&gt;
| Parameters || H, L and d || V(0) || Volume at level 0&lt;br /&gt;
|-&lt;br /&gt;
| Calibration mode || Number of refuels used || Points || Number of points in the table&lt;br /&gt;
|-&lt;br /&gt;
| Offset || Fuel level and litres of refuel 1 || ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The calibration table lists each point with the columns &#039;&#039;&#039;#, Fuel level, V (L)&#039;&#039;&#039; and &#039;&#039;&#039;Zone&#039;&#039;&#039; (zero, offset, refuel 2, refuel 3, full). Tap &#039;&#039;&#039;Chart&#039;&#039;&#039; (top-right corner) to view the volume-versus-fuel-level graph – the curve should rise smoothly, with no sudden jumps.&lt;br /&gt;
&lt;br /&gt;
=== Download CSV File (Both Modes) ===&lt;br /&gt;
&lt;br /&gt;
Tap &#039;&#039;&#039;Download CSV&#039;&#039;&#039; and choose a save location on the phone; the app shows &#039;&#039;&#039;Success&#039;&#039;&#039; with the file path. Use the file to load the table into LIGO MAX, keep installation records, or import it via Manual Calibration.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Item !! Value&lt;br /&gt;
|-&lt;br /&gt;
| File name || yyyyMMdd_HHmmss_&amp;amp;lt;points&amp;amp;gt;points_&amp;amp;lt;tank&amp;amp;gt;_H&amp;amp;lt;H&amp;amp;gt;_L&amp;amp;lt;L&amp;amp;gt;.csv, e.g. 20261103_101530_50points_cylinder_H60.5_L57.5.csv&lt;br /&gt;
|-&lt;br /&gt;
| Columns || No, Volume (liter), Level&lt;br /&gt;
|-&lt;br /&gt;
| Limits || Maximum 200 points; each volume value up to 65,535 L&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Save to Sensor (Sensor Live Mode Only) ===&lt;br /&gt;
&lt;br /&gt;
Tap &#039;&#039;&#039;Save to sensor&#039;&#039;&#039;, select &#039;&#039;&#039;Yes&#039;&#039;&#039; in the &amp;quot;Would you like to save calibration points?&amp;quot; dialog and wait until &amp;quot;Calibration points saved successfully!&amp;quot; appears. For LIGO AIR, the app automatically enables the calibration table on the sensor after writing; no further setup is needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-09-results.png|&#039;&#039;Figure 9. Results page&#039;&#039;&lt;br /&gt;
File:AutoCalib-10-chart.png|&#039;&#039;Figure 10. Calibration chart&#039;&#039;&lt;br /&gt;
File:AutoCalib-11-csv-saved.png|&#039;&#039;Figure 11. CSV file saved&#039;&#039;&lt;br /&gt;
File:AutoCalib-12-save-sensor.png|&#039;&#039;Figure 12. Table saved to the sensor&#039;&#039;&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Error Messages ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;[[File:AutoCalib-08-error.png|frameless|300px]]&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 13. Example of an input error message&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Message !! Cause !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| Tank height H is empty. / Sensor height is empty. || H or L has not been entered || Enter both values in Step 1&lt;br /&gt;
|-&lt;br /&gt;
| Sensor height (…) cannot exceed tank height (…) || L is greater than H || Re-measure H and L&lt;br /&gt;
|-&lt;br /&gt;
| 1st refuel (offset): fuel level end is empty. (or litres added) || The refuel row is incomplete || Fill in all three rows in Step 2&lt;br /&gt;
|-&lt;br /&gt;
| Fuel level offset must be within 1…4093. || Fuel level end of refuel 1 is 0 or too high || Check the reading after refuel 1&lt;br /&gt;
|-&lt;br /&gt;
| Refuel N: fuel level end = X must be greater than fuel level start = Y. || The level after refuel N is not higher than before || Check the reading of refuel N&lt;br /&gt;
|-&lt;br /&gt;
| Refuel N: fuel level end cannot exceed 4095. || The value exceeds the sensor&#039;s measuring range || Enter a value from 0 to 4095&lt;br /&gt;
|-&lt;br /&gt;
| Voffset must be greater than 0 / Refuel N: ΔV must be greater than 0. || Litres added is 0 or negative || Enter the correct litres added&lt;br /&gt;
|-&lt;br /&gt;
| Number of points must be within 2…&amp;amp;lt;max&amp;amp;gt;. || Custom number of points is out of range || Enter a value within the range in Step 3&lt;br /&gt;
|-&lt;br /&gt;
| Total fuel volumn (…) cannot exeed 65535. || Total litres too large || Re-check the litres entered&lt;br /&gt;
|-&lt;br /&gt;
| Cannot export – LIGO MAX accepts at most 200 calibration points. || The table has more than 200 points || Regenerate the table with at most 200 points&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Customer support channels for this app.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039; contact table&lt;br /&gt;
* Website, Email, Phone, Service hours&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Software &amp;amp; Applications]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_BLE_CONFIGURATOR&amp;diff=2123</id>
		<title>LIGO BLE CONFIGURATOR</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_BLE_CONFIGURATOR&amp;diff=2123"/>
		<updated>2026-11-04T07:49:03Z</updated>

		<summary type="html">&lt;p&gt;Admin: edit content of autocalib mode&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
The LIGO BLE Configurator is a cross-platform mobile application for configuring, monitoring, and managing LIGO Bluetooth Low Energy (BLE) fuel level sensors. It runs on Android and iOS and communicates with sensors over Bluetooth Low Energy. The application is developed by SOJI Electronics JSC using Microsoft .NET MAUI.&lt;br /&gt;
&lt;br /&gt;
=== Supported Product Lines ===&lt;br /&gt;
&lt;br /&gt;
From the home screen (Bluetooth Products page) you choose one of three product lines. The available pages and settings adapt to the product you select.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Product !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| LIGO AIR || Wireless sensor || Battery-powered wireless fuel level sensor using Bluetooth 5.2&lt;br /&gt;
|-&lt;br /&gt;
| LIGO PRO BLE / PRO 2(S) || Wired sensor || Wired fuel level sensor with Bluetooth 5.2 for wireless configuration&lt;br /&gt;
|-&lt;br /&gt;
| LIGO AIR ADAPTER || Transceiver || Transceiver device that connects multiple LIGO AIR wireless sensors&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Key Capabilities ===&lt;br /&gt;
&lt;br /&gt;
* Scan for and connect to nearby LIGO BLE sensors&lt;br /&gt;
* View real-time sensor data (fuel level, temperature, capacitance, battery, accelerometer)&lt;br /&gt;
* Calibrate the tank using Set Empty / Set Full points and a multi-point calibration table&lt;br /&gt;
* Configure sensor parameters (output format, transmission interval, address, baud rate, and more)&lt;br /&gt;
* Manage BLE / advanced settings (TX power, advertising interval, BLE mode, logging)&lt;br /&gt;
* Download stored data logs and export them as Excel (.xlsx) files&lt;br /&gt;
* View setting / event logs retrieved from the SOJI server&lt;br /&gt;
* Update sensor firmware &lt;br /&gt;
* Create installation and maintenance reports with photos&lt;br /&gt;
* Manage multiple wireless sensors through the LIGO AIR ADAPTER&lt;br /&gt;
* Protect sensor access with a password, including a password recovery mechanism&lt;br /&gt;
&lt;br /&gt;
=== Navigation Structure ===&lt;br /&gt;
&lt;br /&gt;
After a product is selected, the application uses a three-tab layout at the bottom of the screen:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Tab !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| Connection || Device scanning, connection, and the real-time device dashboard&lt;br /&gt;
|-&lt;br /&gt;
| Settings || Product-specific configuration pages (calibration, parameters, advanced settings, logs, reports)&lt;br /&gt;
|-&lt;br /&gt;
| Help || Online support contacts, application information, and user-guide resource links&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[File:01-products-page.png|center|250px|thumb|Figure 1. Bluetooth Products (home) page]]&lt;br /&gt;
&lt;br /&gt;
== System Requirements ==&lt;br /&gt;
&lt;br /&gt;
=== Operating System ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Platform !! Minimum version&lt;br /&gt;
|-&lt;br /&gt;
| Android || Android 8.0 Oreo (API level 26) or later&lt;br /&gt;
|-&lt;br /&gt;
| iOS || iOS 13.2 or later&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Hardware ===&lt;br /&gt;
&lt;br /&gt;
* A mobile device with Bluetooth Low Energy (BLE) support. BLE hardware is required; the app shows a warning if the device does not support BLE.&lt;br /&gt;
* A camera is used for scanning QR codes and taking report photos (optional features).&lt;br /&gt;
&lt;br /&gt;
=== Permissions ===&lt;br /&gt;
&lt;br /&gt;
The application declares and requests the following permissions. On first use (when the Products page opens), the app requests Bluetooth, Location, and Camera permission.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Permission !! Why it is used&lt;br /&gt;
|-&lt;br /&gt;
| Bluetooth || Read data from and write configuration to LIGO BLE sensors&lt;br /&gt;
|-&lt;br /&gt;
| Location || Required by the operating system for BLE scanning; also used to record the sensor installation location&lt;br /&gt;
|-&lt;br /&gt;
| Camera || Scan a sensor serial-number QR code and take photos for reports&lt;br /&gt;
|-&lt;br /&gt;
| Photos / Storage || Attach photos to reports and save exported data files&lt;br /&gt;
|-&lt;br /&gt;
| Internet / Network || Upload logs and reports and retrieve server-side setting logs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOTICE: Android Location requirement&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On Android, Location Services must be enabled for BLE scanning to work. This is an Android operating-system requirement, not a limitation of the application.&lt;br /&gt;
&lt;br /&gt;
=== Network ===&lt;br /&gt;
&lt;br /&gt;
An internet connection is required only for the server-connected features: uploading data logs and reports to the SOJI server, and viewing setting / event logs retrieved from the server. Scanning, connecting, calibration, parameter configuration, and firmware updates work over Bluetooth without an internet connection.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Installing the Application ===&lt;br /&gt;
&lt;br /&gt;
# Install LIGO BLE Configurator from the Apple App Store (iOS) or Google Play Store (Android), or from the distribution package provided by SOJI Electronics.&lt;br /&gt;
# Open the application after installation completes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:Google-Play.png|LIGO BLE Configurator on Google Play&lt;br /&gt;
File:App-Store.png|LIGO BLE Configurator on App Store&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Granting Permissions ===&lt;br /&gt;
&lt;br /&gt;
When you open the application and reach the Products page, it checks for the permissions it needs and prompts you to grant any that are missing:&lt;br /&gt;
&lt;br /&gt;
# Allow Bluetooth access. If Bluetooth permission is not granted, the app shows a message asking you to allow Bluetooth access and cannot scan for sensors.&lt;br /&gt;
# Allow Location access (used for BLE scanning and to record installation location).&lt;br /&gt;
# Allow Camera access (used for QR scanning and report photos).&lt;br /&gt;
# If Bluetooth is turned off on the device, enable it when prompted.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOTICE: Bluetooth not available&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If the device hardware does not support Bluetooth Low Energy, the app displays the warning &amp;quot;Your phone does not support Bluetooth Low Energy&amp;quot; and scanning is unavailable.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;[ Screenshot to be added: Operating-system permission dialog (Bluetooth / Location / Camera) ]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Please capture this screen and place the image here.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Operating-system permission dialog (Bluetooth / Location / Camera)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Quick Start ==&lt;br /&gt;
&lt;br /&gt;
This section walks through connecting to a sensor and reaching its dashboard for the first time.&lt;br /&gt;
&lt;br /&gt;
=== Select a Product ===&lt;br /&gt;
&lt;br /&gt;
# Open the app to display the Bluetooth Products page.&lt;br /&gt;
# Tap the product line you are working with: LIGO AIR, LIGO PRO BLE / PRO 2(S), or LIGO AIR ADAPTER.&lt;br /&gt;
# The app opens the scan page for that product and begins searching for nearby BLE devices automatically.&lt;br /&gt;
&lt;br /&gt;
=== Scan and Connect ===&lt;br /&gt;
&lt;br /&gt;
The scan page lists discovered devices. Each row shows the device name, signal strength (RSSI), and a fuel-level reading taken from the advertisement data.&lt;br /&gt;
&lt;br /&gt;
* Use the search bar to filter the list by name or serial number.&lt;br /&gt;
* Use the SOJI filter toggle to show only SOJI / LIGO devices.&lt;br /&gt;
* Use the QR scanner to find a specific sensor by scanning its QR code.&lt;br /&gt;
&lt;br /&gt;
# Locate your target device in the list.&lt;br /&gt;
# Tap Connect on the device row to start the BLE connection.&lt;br /&gt;
# When the connection succeeds, the app opens the Device page (dashboard).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:02-scan-page-air.png|Figure 2. Scan page (LIGO AIR)&lt;br /&gt;
File:02-scan-page.png|Figure 3. Scan page (LIGO PRO)&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Open Settings ===&lt;br /&gt;
&lt;br /&gt;
Use the Settings tab to reach the configuration pages for the connected device. The menu contents depend on the product line.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:settings-menu.png|Figure 5. Settings menu (LIGO PRO)&lt;br /&gt;
File:settings-menu-air.png|Figure 6. Settings menu (LIGO AIR)&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Password Prompt ===&lt;br /&gt;
&lt;br /&gt;
LIGO sensors are password protected. The password is not requested at connection time; instead, it is requested the first time you open a password-protected settings page (for example Parameter Settings, Advanced Settings, or Set Empty / Set Full). Enter the password and tap OK to continue.&lt;br /&gt;
&lt;br /&gt;
* The factory default password is 1234. Change it after the initial setup.&lt;br /&gt;
* If you forget the password, use Forgot Password on the prompt to obtain a recovery hash and contact SOJI support. See Section 9.&lt;br /&gt;
&lt;br /&gt;
[[File:password-prompt.png|center|250px|thumb|Figure 4. Password prompt]]&lt;br /&gt;
&lt;br /&gt;
== Features ==&lt;br /&gt;
&lt;br /&gt;
This section summarises the main screens. Detailed configuration steps are in Section 6 (Configuration / Settings).&lt;br /&gt;
&lt;br /&gt;
=== Device Dashboard ===&lt;br /&gt;
&lt;br /&gt;
After connecting, the Device page shows a tank graphic with a fill-level indicator plus live readings. The fields shown depend on the product line.&lt;br /&gt;
&lt;br /&gt;
==== LIGO PRO BLE ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:03-device-page-pro.png|Figure 7. LIGO PRO dashboard (top)&lt;br /&gt;
File:03-device-page-pro-2.png|Figure 8. LIGO PRO dashboard (details)&lt;br /&gt;
File:03-device-page-pro-3.png|LIGO PRO dashboard (further details)&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Fields displayed under the DEVICE section include:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Data source || Source of the displayed data (advertisement or connected)&lt;br /&gt;
|-&lt;br /&gt;
| C_working_compensated || Temperature-compensated working capacitance value&lt;br /&gt;
|-&lt;br /&gt;
| C Min / C Max || Empty / full tank capacitance reference values&lt;br /&gt;
|-&lt;br /&gt;
| OSC frequency || Oscillator frequency reading&lt;br /&gt;
|-&lt;br /&gt;
| Fuel volume (Liter) || Calculated fuel volume based on calibration&lt;br /&gt;
|-&lt;br /&gt;
| Baudrate || Current serial communication baud rate&lt;br /&gt;
|-&lt;br /&gt;
| Filter time (s) || Signal filtering time in seconds&lt;br /&gt;
|-&lt;br /&gt;
| BLE mode || Current Bluetooth operating mode&lt;br /&gt;
|-&lt;br /&gt;
| Address || Sensor network address&lt;br /&gt;
|-&lt;br /&gt;
| Rssi || Received Signal Strength Indicator&lt;br /&gt;
|-&lt;br /&gt;
| Boot counts || Number of sensor power-on cycles&lt;br /&gt;
|-&lt;br /&gt;
| Operating time (hours) || Total sensor operating time&lt;br /&gt;
|-&lt;br /&gt;
| Sensor type || Hardware type identifier&lt;br /&gt;
|-&lt;br /&gt;
| Protocol || Communication protocol (when available)&lt;br /&gt;
|-&lt;br /&gt;
| Sensor message || Status message (color-coded: green = OK, red = error)&lt;br /&gt;
|-&lt;br /&gt;
| Device ID / Serial Number || Identifiers (tap to copy to clipboard)&lt;br /&gt;
|-&lt;br /&gt;
| Boot / Firmware / Hardware version, FW build date || Firmware and hardware identifiers&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== LIGO AIR ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:04-device-page-air.png|Figure 9. LIGO AIR dashboard (top)&lt;br /&gt;
File:04-device-page-air-2.png|Figure 10. LIGO AIR dashboard (details)&lt;br /&gt;
File:04-device-page-air-3.png|LIGO AIR dashboard (further details)&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The LIGO AIR dashboard adds wireless-specific fields not present on the wired LIGO PRO:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Raw data || Unprocessed sensor reading&lt;br /&gt;
|-&lt;br /&gt;
| C_working_compensated, C Min, C Max || Capacitance working and reference values&lt;br /&gt;
|-&lt;br /&gt;
| CNT || Counter / oscillator count value&lt;br /&gt;
|-&lt;br /&gt;
| Vbat || Battery voltage (e.g. X.XX V)&lt;br /&gt;
|-&lt;br /&gt;
| Axis X / Y / Z || Three-axis accelerometer readings&lt;br /&gt;
|-&lt;br /&gt;
| Time/Date || Sensor internal real-time clock value&lt;br /&gt;
|-&lt;br /&gt;
| Rssi, Boot counts, Operating time || Signal strength and usage statistics&lt;br /&gt;
|-&lt;br /&gt;
| Sensor message || Status message (color-coded)&lt;br /&gt;
|-&lt;br /&gt;
| Serial Number / MAC address || Identifiers (tap to copy)&lt;br /&gt;
|-&lt;br /&gt;
| Boot / Firmware / Hardware version, FW build date || Firmware and hardware identifiers&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
Two calibration methods map raw capacitance readings to fuel volume: Set Empty / Set Full (two reference points) and a multi-point calibration table for non-linear tanks. See [https://wiki.sojielectronics.com/index.php?title=LIGO_BLE_CONFIGURATOR&amp;amp;stable=0#Calibration_2 Calibration].&lt;br /&gt;
&lt;br /&gt;
=== Logs ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Data Logs&#039;&#039;&#039; – download historical measurements stored in the sensor and export them as Excel files.&lt;br /&gt;
* &#039;&#039;&#039;Setting Logs / Event Logs&#039;&#039;&#039; – view configuration-change and event history retrieved from the SOJI server.&lt;br /&gt;
&lt;br /&gt;
=== Firmware Update ===&lt;br /&gt;
&lt;br /&gt;
Update the sensor firmware over the air. LIGO PRO uses the Nordic DFU process (.zip files); LIGO AIR and LIGO AIR ADAPTER use the Silicon Labs OTA process (.gbl files). See [https://wiki.sojielectronics.com/index.php?title=LIGO_BLE_CONFIGURATOR&amp;amp;stable=0#Firmware_Update_2 Firmware Update].&lt;br /&gt;
&lt;br /&gt;
=== Reports ===&lt;br /&gt;
&lt;br /&gt;
Create installation and maintenance reports with up to 3 photos each, and view previously created reports. See Section 6.5.&lt;br /&gt;
&lt;br /&gt;
=== Multi-Sensor Management (LIGO AIR ADAPTER) ===&lt;br /&gt;
&lt;br /&gt;
The LIGO AIR ADAPTER aggregates data from multiple LIGO AIR sensors. From the adapter connection page you can add sensors, remove sensors, and refresh their status. See Section 6.&lt;br /&gt;
&lt;br /&gt;
[[File:11-adapter-scan.png|center|250px|thumb|Figure 11. LIGO AIR ADAPTER - connected sensor list]]&lt;br /&gt;
&lt;br /&gt;
== Auto Calibration Table ==&lt;br /&gt;
&lt;br /&gt;
The Auto Calibration feature (CalibTable Generator) builds a complete fuel tank calibration table from only &#039;&#039;&#039;three refuels&#039;&#039;&#039;. Instead of adding fuel in small steps and recording dozens of points by hand, you enter the tank shape, two dimensions, and the fuel level and litres of each refuel. The application then calculates every point of the table. Available from application version 2.2.4.&lt;br /&gt;
&lt;br /&gt;
=== Overview ===&lt;br /&gt;
&lt;br /&gt;
The generator can be used in two modes:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mode !! When to use !! Sensor connection !! Output&lt;br /&gt;
|-&lt;br /&gt;
| Standalone || You already have refuel measurements, or you are preparing a table for LIGO AIR, LIGO MAX, LIGO PRO BLE || Not required || CSV file&lt;br /&gt;
|-&lt;br /&gt;
| Sensor Live || You are calibrating on the vehicle while connected to a LIGO AIR or LIGO PRO BLE / PRO 2(S) sensor || Required || Write directly to the sensor, or CSV file&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Supported tank shapes:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;200&amp;quot; heights=&amp;quot;150&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-tank-parallelepiped.png|Parallelepiped (rectangular box)&lt;br /&gt;
File:AutoCalib-tank-cylinder.png|Cylinder (horizontal)&lt;br /&gt;
File:AutoCalib-tank-dshape.png|D-shape&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Before You Start ===&lt;br /&gt;
&lt;br /&gt;
* The fuel tank is &#039;&#039;&#039;empty&#039;&#039;&#039;, or drained to the lowest possible level.&lt;br /&gt;
* The sensor probe has been cut to length and installed on the tank.&lt;br /&gt;
* You have a way to measure the &#039;&#039;&#039;litres added&#039;&#039;&#039; accurately (a metered pump or a calibrated container).&lt;br /&gt;
* You know the two tank dimensions:&lt;br /&gt;
** &#039;&#039;&#039;H – Tank height&#039;&#039;&#039; (cm): the total inside height of the tank, measured from the bottom.&lt;br /&gt;
** &#039;&#039;&#039;L – Sensor height&#039;&#039;&#039; (cm): the probe length after cutting, from the lower edge of the mounting base to the probe tip.&lt;br /&gt;
* The difference &#039;&#039;&#039;d = H − L&#039;&#039;&#039; is the bottom gap. Fuel below the probe tip cannot be measured by the sensor.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:AutoCalib-dim-cylinder.png|frameless|400px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 23. Tank height H, sensor height L and bottom gap d&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Plan three refuels:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;1st refuel (offset):&#039;&#039;&#039; from an empty tank, fill to about &#039;&#039;&#039;5–10 %&#039;&#039;&#039; of the tank volume.&lt;br /&gt;
# &#039;&#039;&#039;2nd refuel:&#039;&#039;&#039; fill to about &#039;&#039;&#039;40–60 %&#039;&#039;&#039;.&lt;br /&gt;
# &#039;&#039;&#039;3rd refuel:&#039;&#039;&#039; fill to a &#039;&#039;&#039;full tank (100 %)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOTICE: Litres per refuel&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
For each refuel, record only the litres added &#039;&#039;&#039;in that refuel&#039;&#039;&#039;. Do not add up the litres from previous refuels.&lt;br /&gt;
&lt;br /&gt;
=== Opening the Generator ===&lt;br /&gt;
&lt;br /&gt;
==== Standalone Mode ====&lt;br /&gt;
&lt;br /&gt;
# Open the application to display the Bluetooth Products page.&lt;br /&gt;
# Tap &#039;&#039;&#039;CALIBTABLE GENERATOR&#039;&#039;&#039;.&lt;br /&gt;
# The CalibTable Generator page opens. No sensor connection is needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:AutoCalib-01-products-generator.png|frameless|300px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 24. CALIBTABLE GENERATOR on the Bluetooth Products page&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Sensor Live Mode ====&lt;br /&gt;
&lt;br /&gt;
# Connect to a &#039;&#039;&#039;LIGO AIR&#039;&#039;&#039; or &#039;&#039;&#039;LIGO PRO BLE&#039;&#039;&#039; sensor as usual.&lt;br /&gt;
# Open the &#039;&#039;&#039;Settings&#039;&#039;&#039; tab and tap &#039;&#039;&#039;Calibration Table&#039;&#039;&#039;.&lt;br /&gt;
# Tap &#039;&#039;&#039;Auto Calibration&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-02-settings-calibration-table.png|Figure 25. Settings &amp;gt; Calibration Table&lt;br /&gt;
File:AutoCalib-03-calibration-menu.png|Figure 26. Calibration Table menu&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOTICE: Manual Calibration&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;Manual Calibration&#039;&#039;&#039; item in the same menu opens the multi-point calibration table described in [[#Multi-Point Calibration Table|Multi-Point Calibration Table]]. This section covers &#039;&#039;&#039;Auto Calibration&#039;&#039;&#039; only.&lt;br /&gt;
&lt;br /&gt;
The page is the same as in Standalone mode, with one extra line, &#039;&#039;&#039;Fuel level from sensor&#039;&#039;&#039;, which shows the live reading from the connected sensor (see [[#Reading the Fuel Level in Sensor Live Mode|Reading the Fuel Level in Sensor Live Mode]]).&lt;br /&gt;
&lt;br /&gt;
=== Step 1 – Tank Type and Dimensions ===&lt;br /&gt;
&lt;br /&gt;
# Tap the tank shape: &#039;&#039;&#039;Parallelepiped&#039;&#039;&#039;, &#039;&#039;&#039;Cylinder&#039;&#039;&#039; or &#039;&#039;&#039;D-shape&#039;&#039;&#039;.&lt;br /&gt;
# Enter &#039;&#039;&#039;Tank height (H)&#039;&#039;&#039; in cm.&lt;br /&gt;
# Enter &#039;&#039;&#039;Sensor height (L)&#039;&#039;&#039; in cm.&lt;br /&gt;
# The application calculates &#039;&#039;&#039;Bottom gap: d = H − L&#039;&#039;&#039; automatically.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:AutoCalib-04-step1.png|frameless|300px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 27. Step 1 – tank type and dimensions&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
How to measure H and L for each tank shape:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;190&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-dim-parallelepiped.png|Parallelepiped&lt;br /&gt;
File:AutoCalib-dim-cylinder.png|Cylinder&lt;br /&gt;
File:AutoCalib-dim-dshape.png|D-shape&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* L must not be greater than H.&lt;br /&gt;
* Decimal values are accepted, for example 60.5.&lt;br /&gt;
* Tap the ⓘ icon next to a field to show a short explanation.&lt;br /&gt;
&lt;br /&gt;
=== Step 2 – Refuel Calibration ===&lt;br /&gt;
&lt;br /&gt;
The refuel table has three rows, one per refuel:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Row !! Fill target !! Note&lt;br /&gt;
|-&lt;br /&gt;
| 1st refuel (offset) || From empty to 5–10 % || Start with an empty tank&lt;br /&gt;
|-&lt;br /&gt;
| 2nd refuel || To 40–60 % ||&lt;br /&gt;
|-&lt;br /&gt;
| 3rd refuel || To full tank (100 %) ||&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Fill targets for each tank shape:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Tank shape !! 1st refuel: 5–10 % !! 2nd refuel: 40–60 % !! 3rd refuel: full&lt;br /&gt;
|-&lt;br /&gt;
! Parallelepiped&lt;br /&gt;
| [[File:AutoCalib-refuel-parallelepiped-1.png|frameless|180px]] || [[File:AutoCalib-refuel-parallelepiped-2.png|frameless|180px]] || [[File:AutoCalib-refuel-parallelepiped-3.png|frameless|180px]]&lt;br /&gt;
|-&lt;br /&gt;
! Cylinder&lt;br /&gt;
| [[File:AutoCalib-refuel-cylinder-1.png|frameless|180px]] || [[File:AutoCalib-refuel-cylinder-2.png|frameless|180px]] || [[File:AutoCalib-refuel-cylinder-3.png|frameless|180px]]&lt;br /&gt;
|-&lt;br /&gt;
! D-shape&lt;br /&gt;
| [[File:AutoCalib-refuel-dshape-1.png|frameless|180px]] || [[File:AutoCalib-refuel-dshape-2.png|frameless|180px]] || [[File:AutoCalib-refuel-dshape-3.png|frameless|180px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Table columns:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Fuel level start || Filled in automatically from the previous row&#039;s Fuel level end. The first row always starts at 0.&lt;br /&gt;
|-&lt;br /&gt;
| Fuel level end || The fuel level reported by the sensor after this refuel, in sensor units (0 – 4095)&lt;br /&gt;
|-&lt;br /&gt;
| ΔV (L) || The litres added in this refuel only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
For each refuel:&lt;br /&gt;
&lt;br /&gt;
# Fill the tank to the target level for that row.&lt;br /&gt;
# Wait for the fuel surface to settle.&lt;br /&gt;
# Read the fuel level from the sensor and enter it in &#039;&#039;&#039;Fuel level end&#039;&#039;&#039;.&lt;br /&gt;
# Enter the litres added in &#039;&#039;&#039;ΔV (L)&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:AutoCalib-05-step2.png|frameless|300px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 28. Step 2 – refuel calibration&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Tap the ⓘ icon next to a row name to show the fill-target picture for the selected tank shape.&lt;br /&gt;
* While you type a Fuel level end value, the tank picture in Step 1 shows a line with the estimated fill percentage. Use it to check that the value is plausible.&lt;br /&gt;
&lt;br /&gt;
==== Reading the Fuel Level in Sensor Live Mode ====&lt;br /&gt;
&lt;br /&gt;
In Sensor Live mode, the line &#039;&#039;&#039;Fuel level from sensor&#039;&#039;&#039; above the refuel table shows the live sensor reading and a status badge:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Badge !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| Stable (green) || The last three readings differ by no more than 1 unit. The value can be recorded.&lt;br /&gt;
|-&lt;br /&gt;
| Unstable (red) || The fuel surface is still moving. Wait before recording.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
When the badge shows &#039;&#039;&#039;Stable&#039;&#039;&#039;, type the displayed value into &#039;&#039;&#039;Fuel level end&#039;&#039;&#039; of the current refuel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:AutoCalib-06-step2-live.png|frameless|300px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 29. Fuel level from sensor in Sensor Live mode&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Rows Highlighted in Red ====&lt;br /&gt;
&lt;br /&gt;
A refuel row turns red when:&lt;br /&gt;
&lt;br /&gt;
* Fuel level end is not greater than Fuel level start.&lt;br /&gt;
* Fuel level end is greater than 4095.&lt;br /&gt;
* ΔV is 0 or negative.&lt;br /&gt;
&lt;br /&gt;
Correct the values in that row before generating the table.&lt;br /&gt;
&lt;br /&gt;
=== Step 3 – Output ===&lt;br /&gt;
&lt;br /&gt;
# Select the &#039;&#039;&#039;Sensor type&#039;&#039;&#039;. The number of calibration points is set automatically for that sensor.&lt;br /&gt;
# To use a different number of points, select &#039;&#039;&#039;Custom&#039;&#039;&#039; and enter the value in &#039;&#039;&#039;Calibration points (custom)&#039;&#039;&#039;. The points are spread evenly over fuel level 0 – 4095.&lt;br /&gt;
# Tap &#039;&#039;&#039;Generate calibration table&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Mode !! Sensor type options&lt;br /&gt;
|-&lt;br /&gt;
| Standalone || LIGO AIR (50 points), LIGO PRO BLE (30 points), LIGO MAX (100 points), Custom (2 – 200 points)&lt;br /&gt;
|-&lt;br /&gt;
| Sensor Live – LIGO PRO BLE || LIGO PRO BLE (30 points), Custom (2 – 30 points)&lt;br /&gt;
|-&lt;br /&gt;
| Sensor Live – LIGO AIR || LIGO AIR (50 points), Custom (2 – 50 points)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:AutoCalib-07-step3.png|frameless|300px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 30. Step 3 – output settings&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If any input is invalid, a red message box appears above the button. See [[#Auto Calibration Error Messages|Auto Calibration Error Messages]].&lt;br /&gt;
&lt;br /&gt;
=== Results ===&lt;br /&gt;
&lt;br /&gt;
The Results page shows:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Item !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Tank shape || Selected tank shape&lt;br /&gt;
|-&lt;br /&gt;
| Parameters || H, L and d&lt;br /&gt;
|-&lt;br /&gt;
| Calibration mode || Number of refuels used&lt;br /&gt;
|-&lt;br /&gt;
| Offset || Fuel level and litres of the 1st refuel&lt;br /&gt;
|-&lt;br /&gt;
| V(4095) || Volume at the maximum sensor level, i.e. the largest volume the sensor can measure (L)&lt;br /&gt;
|-&lt;br /&gt;
| V(0) || Volume at level 0&lt;br /&gt;
|-&lt;br /&gt;
| Points || Number of points in the table&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The calibration table lists each point with the columns &#039;&#039;&#039;#&#039;&#039;&#039;, &#039;&#039;&#039;Fuel level&#039;&#039;&#039;, &#039;&#039;&#039;V (L)&#039;&#039;&#039; (rounded to whole litres) and &#039;&#039;&#039;Zone&#039;&#039;&#039; (zero, offset, refuel 2, refuel 3, full).&lt;br /&gt;
&lt;br /&gt;
Tap &#039;&#039;&#039;Chart&#039;&#039;&#039; at the top right to plot volume against fuel level. The curve should rise smoothly without sudden steps.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:AutoCalib-09-results.png|Figure 31. Results page&lt;br /&gt;
File:AutoCalib-10-chart.png|Figure 32. Calibration chart&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Download CSV ====&lt;br /&gt;
&lt;br /&gt;
Available in both modes.&lt;br /&gt;
&lt;br /&gt;
# Tap &#039;&#039;&#039;Download CSV&#039;&#039;&#039;.&lt;br /&gt;
# Choose where to save the file on the phone.&lt;br /&gt;
# The application shows &#039;&#039;&#039;Success&#039;&#039;&#039; with the saved file path.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:AutoCalib-11-csv-saved.png|frameless|300px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 33. CSV file saved&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Item !! Value&lt;br /&gt;
|-&lt;br /&gt;
| File name || &amp;lt;code&amp;gt;yyyyMMdd_HHmmss_&amp;amp;lt;points&amp;amp;gt;points_&amp;amp;lt;tank&amp;amp;gt;_H&amp;amp;lt;H&amp;amp;gt;_L&amp;amp;lt;L&amp;amp;gt;.csv&amp;lt;/code&amp;gt;, for example &amp;lt;code&amp;gt;20261103_101530_50points_cylinder_H60.5_L57.5.csv&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| Columns || No, Volume (liter), Level&lt;br /&gt;
|-&lt;br /&gt;
| Limits || At most 200 points; each volume at most 65 535 L&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Use the CSV file to load the calibration table into LIGO MAX, to keep with the installation records, or to import through Manual Calibration.&lt;br /&gt;
&lt;br /&gt;
==== Save to Sensor ====&lt;br /&gt;
&lt;br /&gt;
Available in Sensor Live mode only.&lt;br /&gt;
&lt;br /&gt;
# Tap &#039;&#039;&#039;Save to sensor&#039;&#039;&#039;.&lt;br /&gt;
# Tap &#039;&#039;&#039;Yes&#039;&#039;&#039; on &amp;quot;Would you like to save calibration points?&amp;quot;.&lt;br /&gt;
# Wait while the application shows &#039;&#039;Saving&#039;&#039;. The message &amp;quot;Calibration points saved successfully!&amp;quot; confirms the write.&lt;br /&gt;
&lt;br /&gt;
For &#039;&#039;&#039;LIGO AIR&#039;&#039;&#039;, the application also enables the calibration table on the sensor after writing it. No further setting is needed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:AutoCalib-12-save-sensor.png|frameless|300px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 34. Calibration table saved to the sensor&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Auto Calibration Error Messages ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:AutoCalib-08-error.png|frameless|300px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 35. Example of an input error message&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Message !! Cause !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| Tank height H is empty. / Sensor height is empty. || H or L was not entered || Enter both values in Step 1&lt;br /&gt;
|-&lt;br /&gt;
| Sensor height (…) cannot exceed tank height (…) || L is greater than H || Measure H and L again&lt;br /&gt;
|-&lt;br /&gt;
| 1st refuel (offset): fuel level end is empty. (or litres added) || A refuel row is incomplete || Fill in all three rows in Step 2&lt;br /&gt;
|-&lt;br /&gt;
| Fuel level offset must be within 1…4093. || The 1st refuel Fuel level end is 0 or too high || Check the reading after the 1st refuel&lt;br /&gt;
|-&lt;br /&gt;
| Refuel N: fuel level end = X must be greater than fuel level start = Y. || The level after refuel N is not higher than before || Check the reading for refuel N&lt;br /&gt;
|-&lt;br /&gt;
| Refuel N: fuel level end cannot exceed 4095. || Value above the sensor range || Enter a value from 0 to 4095&lt;br /&gt;
|-&lt;br /&gt;
| Voffset must be greater than 0 / Refuel N: ΔV must be greater than 0. || Litres added is 0 or negative || Enter the litres actually added&lt;br /&gt;
|-&lt;br /&gt;
| Number of points must be within 2…&amp;amp;lt;max&amp;amp;gt;. || Custom point count out of range || Enter a value within the range in [[#Step 3 – Output|Step 3]]&lt;br /&gt;
|-&lt;br /&gt;
| Total fuel volumn (…) cannot exeed 65535. || Total litres too large || Check the litres entered&lt;br /&gt;
|-&lt;br /&gt;
| Cannot export – LIGO MAX accepts at most 200 calibration points. || Table has more than 200 points || Generate the table again with 200 points or fewer&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Configuration / Settings ==&lt;br /&gt;
&lt;br /&gt;
All configuration pages provide Load from Sensor (read current values from the device) and Save to Sensor (write changes to the device).&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
==== Set Empty / Set Full ====&lt;br /&gt;
&lt;br /&gt;
Establishes the empty-tank and full-tank reference points for linear fuel-level calculation. Applicable to LIGO PRO BLE and LIGO AIR.&lt;br /&gt;
&lt;br /&gt;
[[File:06-set-empty-full.png|center|250px|thumb|Figure 12. Set Empty / Set Full page]]&lt;br /&gt;
&lt;br /&gt;
Set the empty point:&lt;br /&gt;
&lt;br /&gt;
# Lower the fuel level to minimum (drain the tank).&lt;br /&gt;
# Open Settings &amp;gt; Set Empty/Full.&lt;br /&gt;
# Watch the live C_working_compensated value and wait for it to stabilise.&lt;br /&gt;
# Tap Set Empty to record the C Min value.&lt;br /&gt;
&lt;br /&gt;
Set the full point:&lt;br /&gt;
&lt;br /&gt;
# Fill the tank to maximum.&lt;br /&gt;
# Wait for the C_working_compensated value to stabilise.&lt;br /&gt;
# Tap Set Full to record the C Max value.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CAUTION: Stable readings required&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Make sure the fuel level is completely stable before tapping Set Empty or Set Full. Unstable readings produce inaccurate calibration.&lt;br /&gt;
&lt;br /&gt;
==== Multi-Point Calibration Table ====&lt;br /&gt;
&lt;br /&gt;
For non-linear tanks, a multi-point table improves accuracy. Applicable to LIGO PRO BLE and LIGO AIR (on LIGO AIR it is reached via the calibration page).&lt;br /&gt;
&lt;br /&gt;
[[File:05-calibration-page.png|center|250px|thumb|Figure 13. Multi-point calibration table]]&lt;br /&gt;
&lt;br /&gt;
# Tap Load from Sensor to read the current table from the device.&lt;br /&gt;
# Tap + to add a calibration point.&lt;br /&gt;
# Enter the known Volume (liter); the Level value comes from the current reading.&lt;br /&gt;
# Repeat for several fuel levels (for example empty, 1/4, 1/2, 3/4, full).&lt;br /&gt;
# Tap - next to a row to remove a point.&lt;br /&gt;
# Tap Save to Sensor to write the table to the device.&lt;br /&gt;
&lt;br /&gt;
Use View Chart to plot the calibration curve and confirm it rises smoothly. On LIGO AIR, Advanced Settings &amp;gt; Recalculate in liters recalculates stored calibration data into liter units.&lt;br /&gt;
&lt;br /&gt;
=== Parameter Settings ===&lt;br /&gt;
&lt;br /&gt;
==== LIGO PRO BLE ====&lt;br /&gt;
&lt;br /&gt;
[[File:07-parameter-settings-pro.png|center|250px|thumb|Figure 14. LIGO PRO parameter settings]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Options / Range !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Automatic transmission || OFF, HEX, ASCII, ASCII-EXT, ASCII-DV || Serial output format&lt;br /&gt;
|-&lt;br /&gt;
| Prefix / Postfix || Free text || Text added before / after messages&lt;br /&gt;
|-&lt;br /&gt;
| Parameter selection || Units (0-4095), Volume (liter), Level (mm), Volume (%) || Output parameter type&lt;br /&gt;
|-&lt;br /&gt;
| Address || 0 - 254 || Sensor network address&lt;br /&gt;
|-&lt;br /&gt;
| Message interval (s) || 0 - 255 || Time between automatic transmissions&lt;br /&gt;
|-&lt;br /&gt;
| Actual length (mm) || 0 or greater || Physical sensor tube length&lt;br /&gt;
|-&lt;br /&gt;
| Level min / Level max || 0.0-8.0 V / 2.0-9.0 V || Analog output voltage range (analog mode)&lt;br /&gt;
|-&lt;br /&gt;
| Baudrate || 2400, 4800, 9600, 14400, 19200, 38400, 115200 || Serial communication speed&lt;br /&gt;
|-&lt;br /&gt;
| Filter time (s) || 0 - 255 || Signal smoothing time&lt;br /&gt;
|-&lt;br /&gt;
| Volume unit (Liter) || 0.1 (liter), 1 (liter) || Fuel volume resolution&lt;br /&gt;
|-&lt;br /&gt;
| C_Min / C_Max (Manual) || 0 - 100000 || Capacitance reference values&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CAUTION: Manual C_Min / C_Max&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Editing C_Min and C_Max manually is not recommended and can cause inaccurate readings. Use the Set Empty / Set Full procedure instead.&lt;br /&gt;
&lt;br /&gt;
==== LIGO AIR ====&lt;br /&gt;
&lt;br /&gt;
[[File:08-parameter-settings-air.png|center|250px|thumb|Figure 15. LIGO AIR parameter settings]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Options / Range !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Advertising protocol || SOJI, ESCORT, MIELTA, TECHNOTON, EUROSEN_V1, EUROSEN_V2 || BLE advertising data format&lt;br /&gt;
|-&lt;br /&gt;
| Maximum level || 1023, 4095 || Maximum digital output value&lt;br /&gt;
|-&lt;br /&gt;
| Filter time || 1 - 15 || Signal smoothing factor&lt;br /&gt;
|-&lt;br /&gt;
| C_Min / C_Max (Manual) || 0 - 300000 || Capacitance reference values&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== LIGO AIR ADAPTER ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Options / Range !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Message interval (s) || 1 - 60 || Interval between data transmissions&lt;br /&gt;
|-&lt;br /&gt;
| Protocol || SOJI, LLS || Output communication protocol&lt;br /&gt;
|-&lt;br /&gt;
| Baudrate || 2400, 4800, 9600, 14400, 19200, 38400, 115200 || Serial communication speed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Advanced Settings ===&lt;br /&gt;
&lt;br /&gt;
==== LIGO PRO BLE (BLE Settings) ====&lt;br /&gt;
&lt;br /&gt;
[[File:09-ble-settings.png|center|250px|thumb|Figure 16. LIGO PRO advanced / BLE settings]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Options !! Description&lt;br /&gt;
|-&lt;br /&gt;
| BLE transmission level || 0 - 10 || Bluetooth transmission power level&lt;br /&gt;
|-&lt;br /&gt;
| BLE mode || Off, On, Auto Off, Auto Readonly || Bluetooth operating mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! BLE mode !! Behavior&lt;br /&gt;
|-&lt;br /&gt;
| On || Bluetooth is always on and configurable&lt;br /&gt;
|-&lt;br /&gt;
| Off || Bluetooth is turned off; reconfiguration requires a wired computer connection (not recommended)&lt;br /&gt;
|-&lt;br /&gt;
| Auto Off || Bluetooth turns off after 24 h of continuous power; restarts after a power cycle&lt;br /&gt;
|-&lt;br /&gt;
| Auto Readonly || Bluetooth becomes read-only after 24 h; writable again after a power cycle&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING: BLE mode Off&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Setting BLE mode to Off disables Bluetooth on the sensor until it is reconfigured through a wired computer connection. Do not select it unless you have alternative configuration access.&lt;br /&gt;
&lt;br /&gt;
The Change your password section lets you set a new password: enter the current password, the new password, confirm it, and tap Change password.&lt;br /&gt;
&lt;br /&gt;
==== LIGO AIR ====&lt;br /&gt;
&lt;br /&gt;
[[File:10-air-advance-settings.png|center|250px|thumb|Figure 17. LIGO AIR advanced settings]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Options / Range !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Advertising interval (s) || 5, 10 || BLE advertisement interval (restricted to 10 when TX power &amp;gt;= 10 dBm)&lt;br /&gt;
|-&lt;br /&gt;
| Measuring interval (s) || 5, 6, 7, 8, 9, 10 || Time between measurement cycles&lt;br /&gt;
|-&lt;br /&gt;
| TX power (dBm) || 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19.5 || BLE transmission power&lt;br /&gt;
|-&lt;br /&gt;
| Log data mode || Disable, Enable || Internal data-logging on/off&lt;br /&gt;
|-&lt;br /&gt;
| Log data interval || 30, 60, 120, 180, 240 (s) || Interval between logged data points&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Utility functions on this page:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Sync time/date || Synchronise the sensor&#039;s internal clock (RTC) with the phone&lt;br /&gt;
|-&lt;br /&gt;
| Recalculate in liters || Convert stored calibration data into liter units&lt;br /&gt;
|-&lt;br /&gt;
| Factory reset || Reset the sensor to factory defaults&lt;br /&gt;
|-&lt;br /&gt;
| Change password || Set a new sensor password&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING: Factory reset&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Factory reset erases all calibration data, parameter settings, and custom configuration. This action cannot be undone. Document your settings first.&lt;br /&gt;
&lt;br /&gt;
==== LIGO AIR ADAPTER ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! Options / Range !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Log data mode || Disable, Enable || Internal data-logging on/off&lt;br /&gt;
|-&lt;br /&gt;
| Log data interval || 30, 60, 120, 180, 240 (s) || Interval between logged data points&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The adapter advanced page also provides Sync time/date, Factory reset, and Change password.&lt;br /&gt;
&lt;br /&gt;
=== Firmware Update ===&lt;br /&gt;
&lt;br /&gt;
[[File:14-dfu-page.png|center|300px|thumb|Figure 18. Firmware upgrade page (three-step process)]]&lt;br /&gt;
&lt;br /&gt;
Firmware update uses a three-step workflow: select the firmware File, select the target Device, then start the Upload and monitor progress.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Product !! Method !! File type&lt;br /&gt;
|-&lt;br /&gt;
| LIGO PRO BLE || Nordic DFU || .zip&lt;br /&gt;
|-&lt;br /&gt;
| LIGO AIR / LIGO AIR ADAPTER || Silicon Labs OTA || .gbl&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
During the LIGO PRO upload the app reports the stages Enable Bootloader, DFU Initialization, Firmware Upload (with upload speed in kB/s and percentage), and DFU Complete. After updating, reconnect and check the Firmware version and FW build date on the dashboard.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING: Do not interrupt a firmware update&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do not interrupt the sensor power supply and keep the phone close to the sensor while the firmware is loading. Interrupting an update may make the sensor inoperable.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;[ Screenshot to be added: Silicon Labs OTA firmware page (LIGO AIR / ADAPTER) ]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Please capture this screen and place the image here.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Silicon Labs OTA firmware page (LIGO AIR / ADAPTER)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Installation &amp;amp; Maintenance Reports ===&lt;br /&gt;
&lt;br /&gt;
[[File:15-installation-report.png|center|250px|thumb|Figure 19. Installation report menu]]&lt;br /&gt;
&lt;br /&gt;
From Settings &amp;gt; Installation Report you can create an installation report, create a maintenance report, or view existing reports.&lt;br /&gt;
&lt;br /&gt;
# Choose Create installation report or Create maintenance report.&lt;br /&gt;
# Fill in the report fields (technician, date, vehicle/plate, device details). Device serial and firmware are filled in automatically when connected.&lt;br /&gt;
# Attach up to 3 photos by taking a new photo or selecting one from the gallery.&lt;br /&gt;
# Tap Create Report to generate the report.&lt;br /&gt;
&lt;br /&gt;
Use View report to search and open a saved report; the report detail page shows the entered values and attached photos.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;[ Screenshot to be added: Create report form and report detail page ]&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Please capture this screen and place the image here.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Create report form and report detail page&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Data Logs ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;350&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:12-data-logs.png|Figure 20. Data logs with downloaded data&lt;br /&gt;
File:12-data-logs-view.png|Data logs — downloaded data view&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Download historical data stored in the device and review it in a table. Applicable to LIGO PRO BLE, LIGO AIR, and LIGO AIR ADAPTER. Select a date range, then download:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Range !! Description&lt;br /&gt;
|-&lt;br /&gt;
| Today / Yesterday || Current or previous day&lt;br /&gt;
|-&lt;br /&gt;
| Week / Month || Past 7 days / past 30 days&lt;br /&gt;
|-&lt;br /&gt;
| Custom || User-defined start and end date/time&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
# Open Settings &amp;gt; Data Logs (for LIGO AIR, via Setting &amp;amp; Data Logs).&lt;br /&gt;
# Make sure the sensor is connected (Download is disabled when disconnected).&lt;br /&gt;
# Select the date range and tap Download.&lt;br /&gt;
# Choose where to save: Device and server or Server only.&lt;br /&gt;
# Save the exported .xlsx (Excel) file to your device.&lt;br /&gt;
&lt;br /&gt;
The downloaded data grid includes columns: Time, C (capacitance), Temp, Volt, Pulse, AccX/AccY/AccZ, Incli (inclination), and Level.&lt;br /&gt;
&lt;br /&gt;
=== Setting / Event Logs ===&lt;br /&gt;
&lt;br /&gt;
[[File:13-event-logs.png|center|250px|thumb|Figure 21. Setting / event logs]]&lt;br /&gt;
&lt;br /&gt;
View configuration-change and event history. For LIGO PRO this is Setting Logs; for LIGO AIR it is reached via Setting &amp;amp; Data Logs &amp;gt; View Setting Logs.&lt;br /&gt;
&lt;br /&gt;
# Open the setting-logs page; the search box is pre-filled with the connected device&#039;s ID. You can also enter an IMEI or serial number.&lt;br /&gt;
# Entries appear as expandable cards. Tap an entry to view details such as the changed property, the new value, and the GPS location at the time of the event.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOTICE: Internet required&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Server-side setting logs require an internet connection. If the network is unavailable the app shows &amp;quot;Network Error!&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Compatibility ==&lt;br /&gt;
&lt;br /&gt;
=== Devices and Operating Systems ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Item !! Supported&lt;br /&gt;
|-&lt;br /&gt;
| Mobile OS || Android 8.0 (API 26) and later; iOS 13.2 and later&lt;br /&gt;
|-&lt;br /&gt;
| Connectivity || Bluetooth Low Energy&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Supported Sensors ===&lt;br /&gt;
&lt;br /&gt;
* LIGO AIR (wireless)&lt;br /&gt;
* LIGO PRO BLE and LIGO PRO 2(S) (wired)&lt;br /&gt;
* LIGO AIR ADAPTER (transceiver for multiple LIGO AIR sensors)&lt;br /&gt;
&lt;br /&gt;
=== Advertising Protocols (LIGO AIR) ===&lt;br /&gt;
&lt;br /&gt;
The LIGO AIR advertising data can be formatted using one of the following protocols, selectable in Parameter Settings:&lt;br /&gt;
&lt;br /&gt;
* SOJI&lt;br /&gt;
* ESCORT&lt;br /&gt;
* MIELTA&lt;br /&gt;
* TECHNOTON&lt;br /&gt;
* EUROSEN_V1&lt;br /&gt;
* EUROSEN_V2&lt;br /&gt;
&lt;br /&gt;
The LIGO AIR ADAPTER output supports the SOJI and LLS protocols.&lt;br /&gt;
&lt;br /&gt;
=== Firmware Update Compatibility ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Product !! Update method !! File type&lt;br /&gt;
|-&lt;br /&gt;
| LIGO PRO BLE || Nordic DFU || .zip&lt;br /&gt;
|-&lt;br /&gt;
| LIGO AIR / LIGO AIR ADAPTER || Silicon Labs OTA || .gbl&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Language ===&lt;br /&gt;
&lt;br /&gt;
The application interface is in English only. A language indicator labelled &amp;quot;EN&amp;quot; appears in the top bar of the Products page; in this version it is an indicator and the application does not provide additional interface languages.&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
=== Connection ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| Device not in the scan list || Enable Bluetooth; enable Location Services (Android); move closer to the sensor; check whether the SOJI filter is hiding it; the sensor BLE mode may be Off&lt;br /&gt;
|-&lt;br /&gt;
| Connection drops during use || Stay close to the sensor; reduce nearby BLE interference&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;Please connect to Sensor!&amp;quot; || You opened a settings page without an active connection - connect first from the Connection tab&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;Adapter is disconnected to your phone!&amp;quot; || The adapter connection was lost - return to the scan page and reconnect&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;Your phone does not support Bluetooth Low Energy&amp;quot; || The device hardware lacks BLE; use a BLE-capable phone&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Password ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| Forgot the password || Tap Forgot Password on the prompt, note the recovery hash, and contact SOJI support (the hash is time-limited)&lt;br /&gt;
|-&lt;br /&gt;
| Password is rejected || Re-enter carefully; the factory default is 1234&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;Please enter the sensor&#039;s password!&amp;quot; || Enter the sensor password when prompted before a write operation&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Calibration ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| Reading will not stabilise || Park on level ground with the engine off and let the fuel settle before Set Empty / Set Full&lt;br /&gt;
|-&lt;br /&gt;
| Value highlighted in red || The value is outside the accepted range shown for that field&lt;br /&gt;
|-&lt;br /&gt;
| Calibration looks wrong || Use Set Empty / Set Full instead of editing C_Min / C_Max manually&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Firmware Update ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| Upload button disabled || Connect to the sensor first and ensure the BLE link is active&lt;br /&gt;
|-&lt;br /&gt;
| Connection lost during update || Keep the phone close, power-cycle the sensor, and retry from step 1&lt;br /&gt;
|-&lt;br /&gt;
| Update fails repeatedly || Re-download the firmware file and confirm it is the correct type for the product&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Logs / Network ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Problem !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| &amp;quot;Network Error!&amp;quot; || Check the internet connection and retry (server logs require internet)&lt;br /&gt;
|-&lt;br /&gt;
| No data for the selected range || Widen the date range or confirm data logging was enabled on the sensor&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Security &amp;amp; Privacy ==&lt;br /&gt;
&lt;br /&gt;
=== Sensor Access Protection ===&lt;br /&gt;
&lt;br /&gt;
* Sensor configuration is protected by a password. The factory default is 1234; change it after the initial setup.&lt;br /&gt;
* The password is requested when you open a password-protected settings page, not at connection time.&lt;br /&gt;
* The app stores sensor passwords in encrypted form on the device.&lt;br /&gt;
&lt;br /&gt;
=== Password Recovery ===&lt;br /&gt;
&lt;br /&gt;
If the password is lost, the Forgot Password option produces a recovery hash. The hash is time-limited and is used together with SOJI support to recover access. Record the new password securely after changing it.&lt;br /&gt;
&lt;br /&gt;
=== Recommendations ===&lt;br /&gt;
&lt;br /&gt;
* Change the default sensor password (1234) after the first connection.&lt;br /&gt;
* Keep a secure record of any new password and of the recovery hash when you change a password.&lt;br /&gt;
* Use BLE mode Off only when you have a wired computer connection available, because it disables Bluetooth access to the sensor.&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&lt;br /&gt;
Support contacts and application information are available on the Help tab.&lt;br /&gt;
&lt;br /&gt;
=== Online Support ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Channel !! Contact&lt;br /&gt;
|-&lt;br /&gt;
| Zalo || (+84) 934 601 358&lt;br /&gt;
|-&lt;br /&gt;
| WhatsApp || (+84) 934 601 358&lt;br /&gt;
|-&lt;br /&gt;
| Email || support@sojielectronics.com&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== About / Company ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field !! Value&lt;br /&gt;
|-&lt;br /&gt;
| Developer || SOJI ELECTRONICS., JSC&lt;br /&gt;
|-&lt;br /&gt;
| Office || C50-LK13, Lot No.09, Le Trong Tan, Duong Noi, Ha Dong, Hanoi, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Factory || Van Khe, Nghia Huong, Quoc Oai, Hanoi, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Website || sojielectronics.com&lt;br /&gt;
|-&lt;br /&gt;
| Tel || (+84) 24 62 932 369&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:16-about-page.png|Figure 22. Help / About page&lt;br /&gt;
File:16-about-page-2.png|Help / About page (details)&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== User-Guide Resources (in the Help tab) ===&lt;br /&gt;
&lt;br /&gt;
* User Guide for LIGO fuel level sensor&lt;br /&gt;
* Quick Install Guide for LIGO fuel level sensor&lt;br /&gt;
* Extending sensor tube instructions&lt;br /&gt;
* Installation video&lt;br /&gt;
&lt;br /&gt;
Tap a resource to open it in the device&#039;s web browser.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Software &amp;amp; Applications]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_User_Guide&amp;diff=2093</id>
		<title>LIGO PRO BLE User Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_User_Guide&amp;diff=2093"/>
		<updated>2026-08-11T03:16:38Z</updated>

		<summary type="html">&lt;p&gt;Admin: Initial Release [release]&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Safety Information ==&lt;br /&gt;
{{Notice&lt;br /&gt;
| title = Installer Qualifications and Safety&lt;br /&gt;
| body = *LIGO PRO BLE measures the accurate fuel level in fuel tanks, therefore the device installation process also needs to be extremely accurate.&lt;br /&gt;
*Installation technicians should be well-trained by specialists or experts in this field before installing the device.&lt;br /&gt;
*Implement protection against electric shocks and electrical fires, labour safety and hygiene.&lt;br /&gt;
*List of installing tools is provided in section APPENDIX A below.&lt;br /&gt;
*List of accessories is provided in section APPENDIX B below.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Product Overview ==&lt;br /&gt;
LIGO PRO BLE sensor is produced and developed by SOJI Electronics Join Stock Company. The device is designed to measure the level of liquid fuels and other non-conductive liquids in vehicle&#039;s tanks and stationary fuel storages, applicable in different fields. The measured values will be t transformed into output signal in the form of: Analog, Frequency, RS232, RS485… and transmitted to a connected external device&lt;br /&gt;
&lt;br /&gt;
Under particular conditions, the device can reach a high accuracy up to 99.5%. At present, on the market there are several lines of sensor using different technologies to measure fuel level such as: magnetically operated switches (reed switches), ultrasound wave (ultrasonic sensor), capacitive sensing (capacitive sensor). Among these ones, capacitive sensing technology is considered to have highest accuracy and greatest lifespan.&lt;br /&gt;
&lt;br /&gt;
== Package Contents ==&lt;br /&gt;
&lt;br /&gt;
10 main items with specific quantities are described below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! No. !! Description !! Qty (pcs)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | LIGO Fuel Level Sensor. Standard lengths: 700, 1000, and 1500mm (for other customized lengths, please contact the manufacturer) || 01&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | Oil filter || 01&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Gasoline-resistant rubber gasket || 01&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Anti-vibration spring when moving || 01&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | 1A Fuse protection || 01&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Self-drilling screw M4.8x32mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Rivet and screw M5x20mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Sealing wire || 02&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | 7m signal wire || 01&lt;br /&gt;
|-&lt;br /&gt;
| 10 || style=&amp;quot;text-align:left;&amp;quot; | Quick installation manual || 01&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0-9V) || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || colspan=&amp;quot;3&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V) || colspan=&amp;quot;3&amp;quot; | 6-40V&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Supply protection (Over-voltage 100 VDC for 2 minutes. Reverse polarity) || colspan=&amp;quot;3&amp;quot; | Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level,% || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the minimum level measurement value || Analog (0...8V) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the maximum level measurement value || Analog (1...9V) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 0...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, s || Continuous || 1...255 || 1...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.1, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity, +8 dBm TX Power&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || 10 || 10 || 10&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Selecting the location to install sensor ===&lt;br /&gt;
&lt;br /&gt;
* Installation location should be as close as possible to the geometric center and placed at the deepest level of the tank.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig1-location.png|frameless|894px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Selecting the location to install sensor&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When being installed, the sensor should not be in contact with reinforcement ribs inside the tank&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig2-two-sensors.png|frameless|730px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Installation of two sensors in one tank allows for significant reduction&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Drilling the hole on fuel tank ===&lt;br /&gt;
&lt;br /&gt;
* Drill out the central bore by bimetal core drill ø38 mm&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig3-drilling.png|frameless|580px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Drilling the selected hole&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Drill out four mounting holes according to the diagram:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig4-mounting-holes.png|frameless|541px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Drill out four mounting holes&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mounting hole diameter depends on the tank material:&lt;br /&gt;
&lt;br /&gt;
* ø 4 mm – for metal tank with wall thickness over 3 mm (cut M5 self screw M4.8)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig5-hole-thick.png|frameless|402px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. Hole thick&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ø 7 mm – for plastic and metal tank with wall thickness up to 3 mm (for rivets)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig6-rivet-nut.png|frameless|372px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 6. Rivet nut M5&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig7-install-rivet.png|frameless|535px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 7. Installing revit nut if tank&#039;s wall is thinner than 3mm&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Measuaring the tank depth and cutting sensor&#039;measuring probe ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig8-measure-depth.png|frameless|615px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 8. Measuring the tank depth&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Cut the sensor&#039;s measuring probe so that its length is 25 mm less than the depth of the tank. Ensure that shearing line is perpendicular to the sensor longitudinal axis&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig9-length.png|frameless|723px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 9. Origin length (L) and length after cutting (Lm)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes: Minimum allowable length of the measuring probe is 150 mm.&lt;br /&gt;
&lt;br /&gt;
=== Installing screen filter ===&lt;br /&gt;
Screen filter is mounted on the measuring probe of sensor to protect measuring probe electrodes from mud and water. Using the filter extends significantly faultless lifetime of the sensor.&lt;br /&gt;
&lt;br /&gt;
Screen filter mounting order: firstly put on fixator, then put on bottom stop and fix it with two side screws. Put the screen filter over bottom stop and fasten it with a fixator locks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig15-screen-filter.png|frameless|747px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 10. Installing screen filter procedue&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing and connecting ===&lt;br /&gt;
&lt;br /&gt;
* Applying oil-and-petrol resistant non-conductive sealant to ruber gasket&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig16-sealant.png|frameless|595px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 11. Applying the sealant to gasket&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Put the sensor into the tank and fix: when fixing with rivets, use a rivet driver when bolting, put on a seal (per bolt), a spacer and a spring washer when fixing to plastic tanks with wall thickness over 3 mm, use vendor furnished self-tapping screws and a seal (per self-tapping screw)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig17-install.png|frameless|663px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 12. Put sensor into the tank&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Connect the sensors to an external device using 7m cable&lt;br /&gt;
&lt;br /&gt;
The wire cable color:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RS232/RS485:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:RS232-RS485.jpg|frameless|756x756px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 13. Wiring diagram of RS232 and RS485 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | TXD/B-&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | RXD/A+&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 6 - 40 VDC&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AF:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig19-wiring-AF.png|frameless|873px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 14. Wiring diagram of Analog and Frequency output signals.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | Out (Analog/Frequency)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 15-37 VDC or 7.5-75V for SP PRO series&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Connect the fuse holder to LIGO sensor power cable (brown wire) in close vicinity to the vehicle power supply circuit&lt;br /&gt;
* Installing the fuse in the fuse holder&lt;br /&gt;
&lt;br /&gt;
=== Sealing ===&lt;br /&gt;
&lt;br /&gt;
* Install the bolt through a hole in the seal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig22-sealing.png|frameless|891px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 15. Sealing&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== Connecting sensor to smartphone and setting ===&lt;br /&gt;
&lt;br /&gt;
To configure the LIGO AIR fuel sensor, download the &#039;&#039;&#039;LIGO BLE Configurator&#039;&#039;&#039; app on &#039;&#039;&#039;Appstore (for IOS) or CH Play (for Android devices).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hereinafter LIGO BLE Configurator is referred to as the &amp;quot;application&amp;quot; or &amp;quot;app&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; For full operation, you must grant all the permissions requested by the app&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-AIR-fig9-app-permissions-1.jpg|Figure 16. Grant all the permissions request by program&lt;br /&gt;
File:LIGO-AIR-fig10-app-permissions-2.jpg|Figure 17. Grant all the permissions request by program&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Grant all the permissions request by program as above&lt;br /&gt;
&lt;br /&gt;
==== Connecting sensor ====&lt;br /&gt;
&lt;br /&gt;
Run the application and select the sensor you want to connect to, as shown in the pictures below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-Connect-PRO-BLE-1.jpg|Figure 18. Selecting sensor type&lt;br /&gt;
File:LIGO-Connect-PRO-BLE-2.jpg|Figure 19. Connecting sensor&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set Empty and Set Full ====&lt;br /&gt;
&lt;br /&gt;
Step 1: Set Full&lt;br /&gt;
&lt;br /&gt;
* Immerse the sensor in fuel until the fuel reaches the sensor flange&lt;br /&gt;
* Wait for C_working value to stablize and then press Set Full button&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig13-set-full.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 20. Set Full&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 2: Set Empty&lt;br /&gt;
&lt;br /&gt;
* Remove the sensor from the fuel and wait for 1-2 minutes allow the fuel drain from the measuring probe and then press Set Empty button.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig14-set-empty.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 21. Set Empty&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Calibration table ===&lt;br /&gt;
&lt;br /&gt;
After installation, the sensor must be calibrated to ensure accurate volume measurement. Calibration table can be performed either on the platform (server) or locally on the sensor.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Platform (Server):&#039;&#039;&#039; The sensor sends raw level values (ranging from 0 to 4095) to the server via the telematic device. The platform uses a calibration table to convert these values into volume (liters).&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Sensor:&#039;&#039;&#039; The sensor directly sends volume values (liters) based on the internal calibration table.&lt;br /&gt;
&lt;br /&gt;
Note: For LIGO AIR sensors, the manufacturer recommends performing calibration table on the platform.&lt;br /&gt;
&lt;br /&gt;
=== Attention! ===&lt;br /&gt;
&lt;br /&gt;
* The manufacturer is not responsible for product availability in case of non-compliance of requirements of this service manual, unauthorized service and repair; if device has damage or traces of opening of head&#039;s body, mechanical damage of the probe or the interface cable, as well as traces of corrosive acids, open flame, high voltage, lightning strikes or other natural factors.&lt;br /&gt;
&lt;br /&gt;
== Certification / Compliance ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[LIGO PRO BLE Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Channel !! Detail&lt;br /&gt;
|-&lt;br /&gt;
| Company || SOJI ELECTRONICS.,JSC&lt;br /&gt;
|-&lt;br /&gt;
| Address || C50-LK13, Lot No. 09, New Urban Area, Le Trong Tan street, Duong Noi Ward, Ha Dong District, Hanoi City, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Tel/Fax || +84 24 62 932 369&lt;br /&gt;
|-&lt;br /&gt;
| Email || contact@sojielectronics.com&lt;br /&gt;
|-&lt;br /&gt;
| Website || [http://sojielectronics.com sojielectronics.com]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
[[Category:LIGO PRO BLE ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=File:Icon-test.svg&amp;diff=2092</id>
		<title>File:Icon-test.svg</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=File:Icon-test.svg&amp;diff=2092"/>
		<updated>2026-07-21T10:51:48Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_User_Guide&amp;diff=2091</id>
		<title>LIGO PRO BLE User Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_User_Guide&amp;diff=2091"/>
		<updated>2026-07-07T07:45:01Z</updated>

		<summary type="html">&lt;p&gt;Admin: change image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Safety Information ==&lt;br /&gt;
{{Notice&lt;br /&gt;
| title = Installer Qualifications and Safety&lt;br /&gt;
| body = *LIGO PRO BLE measures the accurate fuel level in fuel tanks, therefore the device installation process also needs to be extremely accurate.&lt;br /&gt;
*Installation technicians should be well-trained by specialists or experts in this field before installing the device.&lt;br /&gt;
*Implement protection against electric shocks and electrical fires, labour safety and hygiene.&lt;br /&gt;
*List of installing tools is provided in section APPENDIX A below.&lt;br /&gt;
*List of accessories is provided in section APPENDIX B below.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Product Overview ==&lt;br /&gt;
LIGO PRO BLE sensor is produced and developed by SOJI Electronics Join Stock Company. The device is designed to measure the level of liquid fuels and other non-conductive liquids in vehicle&#039;s tanks and stationary fuel storages, applicable in different fields. The measured values will be t transformed into output signal in the form of: Analog, Frequency, RS232, RS485… and transmitted to a connected external device&lt;br /&gt;
&lt;br /&gt;
Under particular conditions, the device can reach a high accuracy up to 99.5%. At present, on the market there are several lines of sensor using different technologies to measure fuel level such as: magnetically operated switches (reed switches), ultrasound wave (ultrasonic sensor), capacitive sensing (capacitive sensor). Among these ones, capacitive sensing technology is considered to have highest accuracy and greatest lifespan.&lt;br /&gt;
&lt;br /&gt;
== Package Contents ==&lt;br /&gt;
&lt;br /&gt;
10 main items with specific quantities are described below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! No. !! Description !! Qty (pcs)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | LIGO Fuel Level Sensor. Standard lengths: 700, 1000, and 1500mm (for other customized lengths, please contact the manufacturer) || 01&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | Oil filter || 01&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Gasoline-resistant rubber gasket || 01&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Anti-vibration spring when moving || 01&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | 1A Fuse protection || 01&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Self-drilling screw M4.8x32mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Rivet and screw M5x20mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Sealing wire || 02&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | 7m signal wire || 01&lt;br /&gt;
|-&lt;br /&gt;
| 10 || style=&amp;quot;text-align:left;&amp;quot; | Quick installation manual || 01&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0-9V) || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || colspan=&amp;quot;3&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V) || colspan=&amp;quot;3&amp;quot; | 6-40V&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Supply protection (Over-voltage 100 VDC for 2 minutes. Reverse polarity) || colspan=&amp;quot;3&amp;quot; | Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level,% || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the minimum level measurement value || Analog (0...8V) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the maximum level measurement value || Analog (1...9V) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 0...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, s || Continuous || 1...255 || 1...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.1, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity, +8 dBm TX Power&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || 10 || 10 || 10&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Selecting the location to install sensor ===&lt;br /&gt;
&lt;br /&gt;
* Installation location should be as close as possible to the geometric center and placed at the deepest level of the tank.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig1-location.png|frameless|894px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Selecting the location to install sensor&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When being installed, the sensor should not be in contact with reinforcement ribs inside the tank&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig2-two-sensors.png|frameless|730px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Installation of two sensors in one tank allows for significant reduction&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Drilling the hole on fuel tank ===&lt;br /&gt;
&lt;br /&gt;
* Drill out the central bore by bimetal core drill ø38 mm&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig3-drilling.png|frameless|580px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Drilling the selected hole&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Drill out four mounting holes according to the diagram:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig4-mounting-holes.png|frameless|541px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Drill out four mounting holes&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mounting hole diameter depends on the tank material:&lt;br /&gt;
&lt;br /&gt;
* ø 4 mm – for metal tank with wall thickness over 3 mm (cut M5 self screw M4.8)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig5-hole-thick.png|frameless|402px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. Hole thick&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ø 7 mm – for plastic and metal tank with wall thickness up to 3 mm (for rivets)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig6-rivet-nut.png|frameless|372px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 6. Rivet nut M5&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig7-install-rivet.png|frameless|535px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 7. Installing revit nut if tank&#039;s wall is thinner than 3mm&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Measuaring the tank depth and cutting sensor&#039;measuring probe ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig8-measure-depth.png|frameless|615px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 8. Measuring the tank depth&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Cut the sensor&#039;s measuring probe so that its length is 25 mm less than the depth of the tank. Ensure that shearing line is perpendicular to the sensor longitudinal axis&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig9-length.png|frameless|723px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 9. Origin length (L) and length after cutting (Lm)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes: Minimum allowable length of the measuring probe is 150 mm.&lt;br /&gt;
&lt;br /&gt;
=== Installing screen filter ===&lt;br /&gt;
Screen filter is mounted on the measuring probe of sensor to protect measuring probe electrodes from mud and water. Using the filter extends significantly faultless lifetime of the sensor.&lt;br /&gt;
&lt;br /&gt;
Screen filter mounting order: firstly put on fixator, then put on bottom stop and fix it with two side screws. Put the screen filter over bottom stop and fasten it with a fixator locks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig15-screen-filter.png|frameless|747px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 10. Installing screen filter procedue&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing and connecting ===&lt;br /&gt;
&lt;br /&gt;
* Applying oil-and-petrol resistant non-conductive sealant to ruber gasket&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig16-sealant.png|frameless|595px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 11. Applying the sealant to gasket&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Put the sensor into the tank and fix: when fixing with rivets, use a rivet driver when bolting, put on a seal (per bolt), a spacer and a spring washer when fixing to plastic tanks with wall thickness over 3 mm, use vendor furnished self-tapping screws and a seal (per self-tapping screw)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig17-install.png|frameless|663px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 12. Put sensor into the tank&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Connect the sensors to an external device using 7m cable&lt;br /&gt;
&lt;br /&gt;
The wire cable color:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RS232/RS485:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:RS232-RS485.jpg|frameless|756x756px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 13. Wiring diagram of RS232 and RS485 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | TXD/B-&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | RXD/A+&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 6 - 40 VDC&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AF:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig19-wiring-AF.png|frameless|873px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 14. Wiring diagram of Analog and Frequency output signals.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | Out (Analog/Frequency)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 15-37 VDC or 7.5-75V for SP PRO series&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Connect the fuse holder to LIGO sensor power cable (brown wire) in close vicinity to the vehicle power supply circuit&lt;br /&gt;
* Installing the fuse in the fuse holder&lt;br /&gt;
&lt;br /&gt;
=== Sealing ===&lt;br /&gt;
&lt;br /&gt;
* Install the bolt through a hole in the seal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig22-sealing.png|frameless|891px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 15. Sealing&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== Connecting sensor to smartphone and setting ===&lt;br /&gt;
&lt;br /&gt;
To configure the LIGO AIR fuel sensor, download the &#039;&#039;&#039;LIGO BLE Configurator&#039;&#039;&#039; app on &#039;&#039;&#039;Appstore (for IOS) or CH Play (for Android devices).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hereinafter LIGO BLE Configurator is referred to as the &amp;quot;application&amp;quot; or &amp;quot;app&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; For full operation, you must grant all the permissions requested by the app&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-AIR-fig9-app-permissions-1.jpg|Figure 16. Grant all the permissions request by program&lt;br /&gt;
File:LIGO-AIR-fig10-app-permissions-2.jpg|Figure 17. Grant all the permissions request by program&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Grant all the permissions request by program as above&lt;br /&gt;
&lt;br /&gt;
==== Connecting sensor ====&lt;br /&gt;
&lt;br /&gt;
Run the application and select the sensor you want to connect to, as shown in the pictures below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-Connect-PRO-BLE-1.jpg|Figure 18. Selecting sensor type&lt;br /&gt;
File:LIGO-Connect-PRO-BLE-2.jpg|Figure 19. Connecting sensor&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set Empty and Set Full ====&lt;br /&gt;
&lt;br /&gt;
Step 1: Set Full&lt;br /&gt;
&lt;br /&gt;
* Immerse the sensor in fuel until the fuel reaches the sensor flange&lt;br /&gt;
* Wait for C_working value to stablize and then press Set Full button&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig13-set-full.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 20. Set Full&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 2: Set Empty&lt;br /&gt;
&lt;br /&gt;
* Remove the sensor from the fuel and wait for 1-2 minutes allow the fuel drain from the measuring probe and then press Set Empty button.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig14-set-empty.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 21. Set Empty&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Calibration table ===&lt;br /&gt;
&lt;br /&gt;
After installation, the sensor must be calibrated to ensure accurate volume measurement. Calibration table can be performed either on the platform (server) or locally on the sensor.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Platform (Server):&#039;&#039;&#039; The sensor sends raw level values (ranging from 0 to 4095) to the server via the telematic device. The platform uses a calibration table to convert these values into volume (liters).&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Sensor:&#039;&#039;&#039; The sensor directly sends volume values (liters) based on the internal calibration table.&lt;br /&gt;
&lt;br /&gt;
Note: For LIGO AIR sensors, the manufacturer recommends performing calibration table on the platform.&lt;br /&gt;
&lt;br /&gt;
=== Attention! ===&lt;br /&gt;
&lt;br /&gt;
* The manufacturer is not responsible for product availability in case of non-compliance of requirements of this service manual, unauthorized service and repair; if device has damage or traces of opening of head&#039;s body, mechanical damage of the probe or the interface cable, as well as traces of corrosive acids, open flame, high voltage, lightning strikes or other natural factors.&lt;br /&gt;
&lt;br /&gt;
== Certification / Compliance ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[LIGO PRO BLE Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Channel !! Detail&lt;br /&gt;
|-&lt;br /&gt;
| Company || SOJI ELECTRONICS.,JSC&lt;br /&gt;
|-&lt;br /&gt;
| Address || C50-LK13, Lot No. 09, New Urban Area, Le Trong Tan street, Duong Noi Ward, Ha Dong District, Hanoi City, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Tel/Fax || +84 24 62 932 369&lt;br /&gt;
|-&lt;br /&gt;
| Email || contact@sojielectronics.com&lt;br /&gt;
|-&lt;br /&gt;
| Website || [http://sojielectronics.com sojielectronics.com]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO BLE ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Datasheet&amp;diff=2090</id>
		<title>LIGO PRO BLE Datasheet</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Datasheet&amp;diff=2090"/>
		<updated>2026-07-07T07:41:23Z</updated>

		<summary type="html">&lt;p&gt;Admin: change image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Product Overview ==&lt;br /&gt;
&lt;br /&gt;
LIGO-PRO-BLE Fuel Level Sensor is produced and developed by SOJI Electronics JSC. The device is designed to measure the level of liquid fuel and other non-conductive liquids in vehicle tanks and stationary fuel storage, applicable in different fields. The measured values will be transmitted to an external device as an output signal such as Analog, Frequency, RS232, or RS485… to connect to an external device.&lt;br /&gt;
&lt;br /&gt;
The purpose of creating a new product was to satisfy the increasing demand of the transport telematics market for improved reliability of the design, data, and ease of installation. Unlike its predecessors, the new sensor is integrated with Bluetooth Low Energy (BLE) making installation more convenient, easier, and faster.&lt;br /&gt;
&lt;br /&gt;
In addition, the new heavy-duty metal structure helps increase protection against electrostatic, electromagnetic and conducted interference. LIGO-PRO-BLE is also designed with IP69K waterproof standards and better shock and vibration resistance.&lt;br /&gt;
&lt;br /&gt;
== Key Features ==&lt;br /&gt;
&lt;br /&gt;
* High accuracy up to 99,5%.&lt;br /&gt;
* Wide range input voltage: 6-40V.&lt;br /&gt;
* Galvanic isolation up to 2500V.&lt;br /&gt;
* Can be optionally cut off or prolonged up to 6000mm.&lt;br /&gt;
* Wide operating temperature range from -40°C to +85°C.&lt;br /&gt;
* Built-in Anti-mud oil filter and anti-vibration springs.&lt;br /&gt;
* IP69K waterproof standard.&lt;br /&gt;
* Integrated BLE technology for configuration via smartphone (available on App Store and Google Play).&lt;br /&gt;
&lt;br /&gt;
== Applications ==&lt;br /&gt;
* Trucks, excavators, heavy construction machines…&lt;br /&gt;
* Marine vessels and electric generators.&lt;br /&gt;
* Off-highway machinery including high-temperature &amp;amp; vibration environments.&lt;br /&gt;
* Industrial oil storage tanks or stationary storage tanks.&lt;br /&gt;
&lt;br /&gt;
== Product Variants / Ordering Information ==&lt;br /&gt;
&lt;br /&gt;
=== Products and accessories ===&lt;br /&gt;
&lt;br /&gt;
10 main items with specific quantities are described below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! No. !! Description !! Qty (pcs)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | LIGO Fuel Level Sensor. Standard lengths: 700, 1000, and 1500mm (for other customized lengths, please contact the manufacturer) || 01&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | Oil filter || 01&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Gasoline-resistant rubber gasket || 01&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Anti-vibration spring when moving || 01&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | 1A Fuse protection || 01&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Self-drilling screw M4.8x32mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Rivet and screw M5x20mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Sealing wire || 02&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | 7m signal wire || 01&lt;br /&gt;
|-&lt;br /&gt;
| 10 || style=&amp;quot;text-align:left;&amp;quot; | Quick installation manual || 01&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Technical Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0-9V) || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || colspan=&amp;quot;3&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V) || colspan=&amp;quot;3&amp;quot; | 6-40V&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Supply protection (Over-voltage 100 VDC for 2 minutes. Reverse polarity) || colspan=&amp;quot;3&amp;quot; | Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level,% || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the minimum level measurement value || Analog (0...8V) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the maximum level measurement value || Analog (1...9V) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 0...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, s || Continuous || 1...255 || 1...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.1, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity, +8 dBm TX Power&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || 10 || 10 || 10&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Dimensions ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-BLE-dimensions.png|frameless|703px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. LIGO-PRO-BLE&#039;s overall dimensions (cm)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wiring Connection ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AF (Analog &amp;amp; Frequency)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-BLE-wiring-AF.png|frameless|636px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Wiring diagram of Analog and Frequency output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RS232&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-BLE-wiring-RS232.png|frameless|642px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Wiring diagram of RS232 output signals&#039;&#039;&#039;RS485&#039;&#039;&#039;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&#039;&#039;&#039;RS485&#039;&#039;&#039;&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:BLE-RS485.jpg|frameless|656x656px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Wiring diagram of RS485 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Connector ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-BLE-connector.png|frameless|624px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. The details of connector pins&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex;gap:20px;justify-content:center;flex-wrap:wrap;align-items:flex-start;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;margin:0;&amp;quot;&lt;br /&gt;
|+ AF/ RS232&lt;br /&gt;
! No !! Description !! Color of the wire&lt;br /&gt;
|-&lt;br /&gt;
| 1 || GND || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 2 || NC (Not connected) || &#039;&#039;&#039;NC&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 3 || VCC (6-40V) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || TXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Analog Output || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#fff;border:1px solid #999;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;White&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 6 || RXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#7b3f00;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Brown&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;margin:0;&amp;quot;&lt;br /&gt;
|+ RS485&lt;br /&gt;
! No !! Description !! Color of the wire&lt;br /&gt;
|-&lt;br /&gt;
| 1 || GND || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 2 || RS485 (B) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#2e8b2e;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Green&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 3 || VCC (6-40V) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || TXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 5 || RS485 (A) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 6 || RXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#7b3f00;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Brown&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Compliance and Certifications ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[LIGO PRO BLE Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO BLE ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=File:BLE-RS485.jpg&amp;diff=2089</id>
		<title>File:BLE-RS485.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=File:BLE-RS485.jpg&amp;diff=2089"/>
		<updated>2026-07-07T07:39:09Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;upload&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_User_Guide&amp;diff=2088</id>
		<title>LIGO PRO User Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_User_Guide&amp;diff=2088"/>
		<updated>2026-07-07T07:34:50Z</updated>

		<summary type="html">&lt;p&gt;Admin: change content&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Safety Information ==&lt;br /&gt;
{{Notice&lt;br /&gt;
| title = Installer Qualifications and Safety&lt;br /&gt;
| body = *LIGO SP Fuel Level Sensor measures the accurate fuel level in fuel tanks, therefore the device installation process also needs to be extremely accurate.&lt;br /&gt;
*Installation technicians should be well-trained by specialists or experts in this field before installing the device.&lt;br /&gt;
*Implement protection against electric shocks and electrical fires, labour safety and hygiene.&lt;br /&gt;
*List of installing tools is provided in section APPENDIX A below.&lt;br /&gt;
*List of accessories is provided in section APPENDIX B below.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Product Overview ==&lt;br /&gt;
LIGO Fuel level sensor is produced and developed by SOJI Electronics Join Stock Company. The device is designed to measure the level of liquid fuels and other non-conductive liquids in vehicle&#039;s tanks and stationary fuel storages, applicable in different fields. The measured values will be t transformed into output signal in the form of: Analog, Frequency, RS232, RS485… and transmitted to a connected external device&lt;br /&gt;
&lt;br /&gt;
Under particular conditions, the device can reach a high accuracy up to 99.5%. At present, on the market there are several lines of sensor using different technologies to measure fuel level such as: magnetically operated switches (reed switches), ultrasound wave (ultrasonic sensor), capacitive sensing (capacitive sensor). Among these ones, capacitive sensing technology is considered to have highest accuracy and greatest lifespan.&lt;br /&gt;
&lt;br /&gt;
== Package Contents ==&lt;br /&gt;
&lt;br /&gt;
=== Apendix A ===&lt;br /&gt;
&lt;br /&gt;
List of equipment for fuel level sensors installation&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! STT !! Descriptions !! Qty&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | Bimetal core drill ø38 mm || 1&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | Core drill shank || 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Metal drill ø7 mm &amp;amp; ø4 mm || 1&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Hacksaw || 1&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | T Spanner 8mm Single Sided || 1&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Tap M5 with holder || 1&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Snap-seal for bolt/self-tapping screw || 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Laptop || 1&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | DC power supply unit 12V-24V or battery 12V || 1&lt;br /&gt;
|-&lt;br /&gt;
| 10 || style=&amp;quot;text-align:left;&amp;quot; | Measuring container || 1&lt;br /&gt;
|-&lt;br /&gt;
| 11 || style=&amp;quot;text-align:left;&amp;quot; | Fuel || 1&lt;br /&gt;
|-&lt;br /&gt;
| 12 || style=&amp;quot;text-align:left;&amp;quot; | Gauging container || 1&lt;br /&gt;
|-&lt;br /&gt;
| 19 || style=&amp;quot;text-align:left;&amp;quot; | USB to RS232 cable || 1&lt;br /&gt;
|-&lt;br /&gt;
| 20 || style=&amp;quot;text-align:left;&amp;quot; | Tape mesaure || 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Apendix B ===&lt;br /&gt;
&lt;br /&gt;
Sensor LIGO SP and all the accessories&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-accessories.png|frameless|782px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Sensor LIGO SP and all the accessories&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! STT !! Description !! Qty&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | LIGO Fuel Level Sensor. Standard lengths: 700, 1000, 1500mm (for other customized lengths, please contact the manufacturer) || 01&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | 7m cable || 01&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Gasoline resistant rubber gasket || 01&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Oil filter || 01&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | Anti-vibration spring || 01&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Fuse 2A || 01&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Sealing cord || 02&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Rivet and screw M5x20mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | Self-drilling screw M4.8x32mm || 04&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&lt;br /&gt;
=== Technical specifications of the LIGO-SP product ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L),mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0…9V), Frequency (500… 2000Hz). || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || 9600 || colspan=&amp;quot;2&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V) || 15-37 || 12-37 || 12-37&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP67&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level,% || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the minimum level measurement value || Analog (0...8V); Frequency (500...1500 Hz) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the maximum level measurement value || Analog (1...9V); Frequency (1000...2000 Hz) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, (s) || colspan=&amp;quot;3&amp;quot; | 0...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, (s) || Continuous || 1...60 || 1...60&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±2&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, (years)) || colspan=&amp;quot;3&amp;quot; | 8&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Technical specifications of the LIGO-SP PRO product ===&lt;br /&gt;
&lt;br /&gt;
* LIGO-SP PRO product is designed for devices having wide input voltage (7-75V), the voltage of the internal sensing printed circuit board isolated from outside is 2500V. The internal temperature sensor has a very low error at &amp;lt;0.6°С. The product is designed to operate in extreme environment.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L),mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0…9), Frequency (500… 2000Hz). || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || 9600 || colspan=&amp;quot;2&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V)&amp;lt;br&amp;gt;2500V internal isolated || colspan=&amp;quot;3&amp;quot; | 7-75&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP67&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level,% || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the minimum level measurement value. || Analog (0...8V); Frequency (500...1500 Hz) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the maximum level measurement value || Analog (1...9V); Frequency (1000...2000 Hz) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling time, (s) || colspan=&amp;quot;3&amp;quot; | 0...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, (s) || continuous || 1...60 || 1...60&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±0.6&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, (years) || colspan=&amp;quot;3&amp;quot; | 10&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Selecting the location to install sensor ===&lt;br /&gt;
&lt;br /&gt;
* Installation location should be as close as possible to the geometric center and placed at the deepest level of the tank.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig1-location.png|frameless|894px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Selecting the location to install sensor&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When being installed, the sensor should not be in contact with reinforcement ribs inside the tank&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig2-two-sensors.png|frameless|730px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Installation of two sensors in one tank allows for significant reduction&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Drilling the hole on fuel tank ===&lt;br /&gt;
&lt;br /&gt;
* Drill out the central bore by bimetal core drill ø38 mm&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig3-drilling.png|frameless|580px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Drilling the selected hole&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Drill out four mounting holes according to the diagram:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig4-mounting-holes.png|frameless|541px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Drill out four mounting holes&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mounting hole diameter depends on the tank material:&lt;br /&gt;
&lt;br /&gt;
* ø 4 mm – for metal tank with wall thickness over 3 mm (cut M5 self screw M4.8)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig5-hole-thick.png|frameless|402px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. Hole thick&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ø 7 mm – for plastic and metal tank with wall thickness up to 3 mm (for rivets)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig6-rivet-nut.png|frameless|372px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 6. Rivet nut M5&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig7-install-rivet.png|frameless|535px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 7. Installing revit nut if tank&#039;s wall is thinner than 3mm&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Measuaring the tank depth and cutting sensor&#039;measuring probe ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig8-measure-depth.png|frameless|615px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 8. Measuring the tank depth&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Cut the sensor&#039;s measuring probe so that its length is 25 mm less than the depth of the tank. Ensure that shearing line is perpendicular to the sensor longitudinal axis&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig9-length.png|frameless|723px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 9. Origin length (L) and length after cutting (Lm)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes: Minimum allowable length of the measuring probe is 150 mm.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== On mobile platform ===&lt;br /&gt;
&lt;br /&gt;
Connecting the sensor to LIGO mobile configurator&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-mobile-connect.png|frameless|600px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 9. Connecting the sensor to the LIGO mobile configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Configuring and managing the appropriate settings for the LiGO device sensor is essential to ensure its proper, stable, and accurate operation. It also allows for continuous monitoring of the device&#039;s operational status, potential failures (whether caused by the sensor itself or external factors), and overall lifespan.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-mobile-ui.png|frameless|444px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 10. Device&#039;s management, setting up and configuration software interface&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On PC Device ===&lt;br /&gt;
==== Installing software and connecting configuration tool ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Preparation:&#039;&#039;&#039;&lt;br /&gt;
* Laptop or PC&lt;br /&gt;
* Power supply 12 -24VDC or battery 12V&lt;br /&gt;
* USB to RS232 cable.&lt;br /&gt;
* Configuration tool from SOJI ELECTRONICS (We recommend to upgrage to the latest possible version)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig10-connect.png|frameless|969px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 10. Connecting USB to RS232 cable and configuration tool to laptop or PC&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
12-24VDC&lt;br /&gt;
&lt;br /&gt;
==== Download and install software. Downloading and installing softwares ====&lt;br /&gt;
&lt;br /&gt;
===== Installing USB to RS232 driver =====&lt;br /&gt;
&lt;br /&gt;
* Installing driver for USB to RS232 cable&lt;br /&gt;
* After finishing right click and check to ensure the driver is installed successful &amp;quot;Computer&amp;quot; &amp;gt;&amp;gt; &amp;quot;Manager&amp;quot; &amp;gt;&amp;gt; &amp;quot;Device Manager&amp;quot; &amp;gt;&amp;gt; &amp;quot;Port (COM &amp;amp; LPT)&amp;quot; as follow the picture bellow, otherwise, please check the cable and cable driver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig11-driver.png|frameless|678px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 11. Installing USB to RS232 driver successful&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Installing sensor software configuration =====&lt;br /&gt;
&lt;br /&gt;
* Visit our website: [http://sojielectronics.com/support/download/ http://sojielectronics.com/support/download/] and download setup file &amp;quot;Setup Utility Program &amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig12-config-tool.png|frameless|710px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 12. Sensor configuration tool&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Parameters descriptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Load config:&#039;&#039;&#039; Load configurations from sensor to PC. Note: user must load configurations from sensor before changing configurations on PC.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Save config:&#039;&#039;&#039; Save configurations from PC to sensor&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Update firmware:&#039;&#039;&#039; Upgrade new firmware for sensor (visit www.sojielectronics.com for the latest firmware version)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Set full:&#039;&#039;&#039; Set Full for maximum fuel level calibration&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Set Empty:&#039;&#039;&#039; Set Empty for minimum fuel level calibration&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Level Min:&#039;&#039;&#039; Configure LIGO SP-AF output voltage or frequency range according to the voltage or frequency range of the tracking device input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Level Max:&#039;&#039;&#039; Configure LIGO SP-AF output voltage or frequency range according to the voltage or frequency range of the tracking device input&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Output type:&#039;&#039;&#039; Selecting voltage or frequency output (used only for AF sensor)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Filter time:&#039;&#039;&#039; Setting output signal processing time (0-255). Default is 60 seconds.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Automatic transmission mode:&#039;&#039;&#039; Automatic transmission mode applied only for RS232/RS485 defines sensor output message type:&lt;br /&gt;
* Off – no automatic message transmission, sensor waits for tracking device request;&lt;br /&gt;
* HEX - automatic message transmission in binary format (used by default);&lt;br /&gt;
* ASCII - automatic message transmission in text format;&lt;br /&gt;
* ASCII EXT – automatic message transmission in extended text format. Additional Prefix and Postfix configurable parameters are available for this mode to insert required header or ending of the message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Message interval:&#039;&#039;&#039; Time period the sensor automatically send output message to the tracking device. Parameter value range is 1…255 seconds with 1 second step. Default value is 1 second.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actual length:&#039;&#039;&#039; The actual length of sensor probe&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Address (0-254):&#039;&#039;&#039; Set the network address for the sensor. When several sensors are connected to one external device, they should have a unique network address.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Parameter selection:&#039;&#039;&#039; Selection of output value type for sensor data&lt;br /&gt;
&lt;br /&gt;
One of the following output value types available for RS232 and RS485 sensor&lt;br /&gt;
* Fuel level in standard (normalized) units (0...1000).&lt;br /&gt;
* Fuel level in millimeters (mm), 0.1 mm step.&lt;br /&gt;
* Fuel volume in liters (L), 0.1 L step.&lt;br /&gt;
* Fuel volume in percentage (%), 0.4% step.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Output:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OSC frequency:&#039;&#039;&#039; Initial measuring generator frequency (Hz)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Data output:&#039;&#039;&#039; Data output (0-4095)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sensor message:&#039;&#039;&#039; Sensor working message if sensor message is none sensor works fine, otherwise malfunction codes are displayed as table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sensor message !! Transcript of the malfunction Code !! Possible solution&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 255 or 254 || Calibration error || Check if the measuring probe actual size value is inserted correctly and (or) re-calibrate the sensor&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 253 || Short circuit in measuring probe tubes || Wash the measuring probe tubes with clean fuel, clean fuel tank of mud and water.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 252 || Calibration error || Check if the measuring probe actual size value is inserted correctly and (or) re-calibrate the sensor&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 251 || Hardware failure || Contact supplier&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 250 || Calibration error || Check if the measuring probe actual size value is inserted correctly and (or) re-calibrate the sensor.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Calibration Table:&#039;&#039;&#039; This entry is used to record fuel tank calibration into the sensor internal memory. The table is filled out with the data achieved during fuel tank calibration procedure. The data is entered as a table of correspondence between measured fuel level value (Fuel level (uint) field) and fuel volume in the tank (Volume (L) field). Recommended number of table entries is 15. Max possible is 30 entries.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Enable AutoClib feature:&#039;&#039;&#039; Sensor automatically calibrates Min, Max levels after cutting. User does not need to reconfigure after cutting.&lt;br /&gt;
&lt;br /&gt;
===== Min/Max Calibration =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;a) Set Empty level&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Keeping sensor apart from fuel and set Empty level&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig13-set-empty.png|frameless|689px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 13. Setting Empty level.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;b) Set Full level&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Fill the measuring container with fuel.&lt;br /&gt;
* Immerse the sensor in the fuel to the full length of the measuring probe and set Full level&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig14-set-full.png|frameless|655px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 14. Setting Full level&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;c) Installing screen filter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Screen filter is mounted on the measuring probe of sensor to protect measuring probe electrodes from mud and water. Using the filter extends significantly faultless lifetime of the sensor.&lt;br /&gt;
&lt;br /&gt;
Screen filter mounting order: firstly put on fixator, then put on bottom stop and fix it with two side screws. Put the screen filter over bottom stop and fasten it with a fixator locks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig15-screen-filter.png|frameless|747px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 15. Installing screen filter procedue&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;d) Installing and connecting&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Applying oil-and-petrol resistant non-conductive sealant to ruber gasket&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig16-sealant.png|frameless|595px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 16. Applying the sealant to gasket&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Put the sensor into the tank and fix: when fixing with rivets, use a rivet driver when bolting, put on a seal (per bolt), a spacer and a spring washer when fixing to plastic tanks with wall thickness over 3 mm, use vendor furnished self-tapping screws and a seal (per self-tapping screw)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig17-install.png|frameless|663px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 17. Put sensor into the tank&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Connect the sensors to an external device using 7m cable&lt;br /&gt;
&lt;br /&gt;
The wire cable color:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RS232/RS485:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:RS232-RS485.jpg|frameless|756x756px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 18. Wiring diagram of RS232 and RS485 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | RXD/B-&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | TXD/A+&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 12-37 VDC or 7.5-75V for SP PRO series&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AF:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig19-wiring-AF.png|frameless|873px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 19. Wiring diagram of Analog and Frequency output signals.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | Out (Analog/Frequency)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 15-37 VDC or 7.5-75V for SP PRO series&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Connect the fuse holder to LIGO sensor power cable (brown wire) in close vicinity to the vehicle power supply circuit&lt;br /&gt;
* Installing the fuse in the fuse holder&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;e) Calibration&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Launch Sensor &amp;gt;&amp;gt; load Config &amp;gt;&amp;gt; From Sensor &amp;gt;&amp;gt; Calibration Table&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig20-calib-table.png|frameless|727px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 20. Calibration table&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Pump fuel in the tank, the amount of pumped fuel depends on the tank&#039;s volume (at least 20 times of pumping)&lt;br /&gt;
* After pumping, wait for a while (about 2 minutes) for the output data to be stable, then select &amp;quot;add a row&amp;quot; and insert the actual pumped liters in &amp;quot;liter&amp;quot; box, input the data given by sensor into &amp;quot;Data&amp;quot; box. User can click on the button to sync up data, or read data in the &amp;quot;output&amp;quot; tab as the image below.&lt;br /&gt;
* Repeat the above steps until the oil tank is full (Attention: It is recommended to write down the information on the paper to compare with input data and avoid mistaking during the process)&lt;br /&gt;
* After completing the above process, select &amp;quot;Volume (liter)&#039;&#039; as the output data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig21-param.png|frameless|464px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 21. Parameter selection&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Finally, click on the &amp;quot;Sensor&amp;quot; button at the top left corner -&amp;gt; Save config -&amp;gt; To sensor to save data for the sensor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention:&#039;&#039;&#039;&lt;br /&gt;
* The time between each pumping must be at least 2 minutes apart or wait for measured values to be stable, then comes the next pumping.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;f) Sealing&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Install the bolt through a hole in the seal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig22-sealing.png|frameless|891px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 22. Sealing&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Quick installation without PC ====&lt;br /&gt;
&lt;br /&gt;
This method is only applicable to configure Min and Max level.&lt;br /&gt;
&lt;br /&gt;
===== Preparation before setting up =====&lt;br /&gt;
&lt;br /&gt;
* The sensor&#039;s auto-recognisation mode needs to be turned on by checking the &amp;quot;Enable AutoCalib Feature&amp;quot; box.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig24-autocalib.png|frameless|865px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 24. Turning on the auto-recognisation mode before installing&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* External devices such as GPS tracking or Dataloger… must have the software integrated with the configurator. The configuration is performed directly on the device.&lt;br /&gt;
&lt;br /&gt;
===== Automatically setting up and configuring the sensor after cutting =====&lt;br /&gt;
&lt;br /&gt;
* After measuring and cutting sensor, remove iron filings and clean burrs between the tubes, then follow steps below:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039; Do not allow the sensor to contact oil.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039; Ensure 12-24VDC stable power supply for the sensor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 3:&#039;&#039;&#039; Make sure the sensor is under stable condition within 30-60s. The sensor will automatically recognise Min and Max level.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 4:&#039;&#039;&#039; Cut the power and reboot. The sensor now recognizes new configurations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig25-auto-config.png|frameless|634px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 25. Leave the sensor in the air, provide with a stable power supply in 30-60s to configure &amp;quot;Min&amp;quot; level.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
- In case of improper operation and the sensor cannot recognise properly the actual Min, Max level, user should reconfigure using a PC.&lt;br /&gt;
&lt;br /&gt;
==== Connecting multiple sensors together ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;(Only applicable to RS232 and RS485)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In some situations, there are oil tanks with specific dimensions, interconnected oil tanks, or oil tanks that are either too long or too large. Using just one sensor to measure the accurate fuel level inside these oil tanks may not be feasible. Hence, there is a need to connect two or more sensors together through a data processor called SUM-DATA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-multi-sensor-tank.png|frameless|728px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 26. Oil tank with particular dimensions connecting multiple sensors together&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Connection diagram of the processor SUM-DATA for multiple sensors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-sumdata-2sensors.png|frameless|648px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 27: Connecting two sensors by 01 processor SUM-DATA&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-sumdata-multi.png|frameless|707px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 28: Connecting multiple sensors together by multiple processors SUM-DATA&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Operation ==&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
== Maintenance ==&lt;br /&gt;
&lt;br /&gt;
== Certification / Compliance ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[LIGO PRO Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Channel !! Detail&lt;br /&gt;
|-&lt;br /&gt;
| Company || SOJI ELECTRONICS.,JSC&lt;br /&gt;
|-&lt;br /&gt;
| Address || C50-LK13, Lot No. 09, New Urban Area, Le Trong Tan street, Duong Noi Ward, Ha Dong District, Hanoi City, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Tel/Fax || +84 24 62 932 369&lt;br /&gt;
|-&lt;br /&gt;
| Email || contact@sojielectronics.com&lt;br /&gt;
|-&lt;br /&gt;
| Website || [http://sojielectronics.com sojielectronics.com]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_User_Guide&amp;diff=2087</id>
		<title>LIGO PRO User Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_User_Guide&amp;diff=2087"/>
		<updated>2026-07-07T07:33:56Z</updated>

		<summary type="html">&lt;p&gt;Admin: change image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Safety Information ==&lt;br /&gt;
{{Notice&lt;br /&gt;
| title = Installer Qualifications and Safety&lt;br /&gt;
| body = *LIGO SP Fuel Level Sensor measures the accurate fuel level in fuel tanks, therefore the device installation process also needs to be extremely accurate.&lt;br /&gt;
*Installation technicians should be well-trained by specialists or experts in this field before installing the device.&lt;br /&gt;
*Implement protection against electric shocks and electrical fires, labour safety and hygiene.&lt;br /&gt;
*List of installing tools is provided in section APPENDIX A below.&lt;br /&gt;
*List of accessories is provided in section APPENDIX B below.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Product Overview ==&lt;br /&gt;
LIGO Fuel level sensor is produced and developed by SOJI Electronics Join Stock Company. The device is designed to measure the level of liquid fuels and other non-conductive liquids in vehicle&#039;s tanks and stationary fuel storages, applicable in different fields. The measured values will be t transformed into output signal in the form of: Analog, Frequency, RS232, RS485… and transmitted to a connected external device&lt;br /&gt;
&lt;br /&gt;
Under particular conditions, the device can reach a high accuracy up to 99.5%. At present, on the market there are several lines of sensor using different technologies to measure fuel level such as: magnetically operated switches (reed switches), ultrasound wave (ultrasonic sensor), capacitive sensing (capacitive sensor). Among these ones, capacitive sensing technology is considered to have highest accuracy and greatest lifespan.&lt;br /&gt;
&lt;br /&gt;
== Package Contents ==&lt;br /&gt;
&lt;br /&gt;
=== Apendix A ===&lt;br /&gt;
&lt;br /&gt;
List of equipment for fuel level sensors installation&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! STT !! Descriptions !! Qty&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | Bimetal core drill ø38 mm || 1&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | Core drill shank || 1&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Metal drill ø7 mm &amp;amp; ø4 mm || 1&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Hacksaw || 1&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | T Spanner 8mm Single Sided || 1&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Tap M5 with holder || 1&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Snap-seal for bolt/self-tapping screw || 1&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Laptop || 1&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | DC power supply unit 12V-24V or battery 12V || 1&lt;br /&gt;
|-&lt;br /&gt;
| 10 || style=&amp;quot;text-align:left;&amp;quot; | Measuring container || 1&lt;br /&gt;
|-&lt;br /&gt;
| 11 || style=&amp;quot;text-align:left;&amp;quot; | Fuel || 1&lt;br /&gt;
|-&lt;br /&gt;
| 12 || style=&amp;quot;text-align:left;&amp;quot; | Gauging container || 1&lt;br /&gt;
|-&lt;br /&gt;
| 19 || style=&amp;quot;text-align:left;&amp;quot; | USB to RS232 cable || 1&lt;br /&gt;
|-&lt;br /&gt;
| 20 || style=&amp;quot;text-align:left;&amp;quot; | Tape mesaure || 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Apendix B ===&lt;br /&gt;
&lt;br /&gt;
Sensor LIGO SP and all the accessories&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-accessories.png|frameless|782px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Sensor LIGO SP and all the accessories&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! STT !! Description !! Qty&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | LIGO Fuel Level Sensor. Standard lengths: 700, 1000, 1500mm (for other customized lengths, please contact the manufacturer) || 01&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | 7m cable || 01&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Gasoline resistant rubber gasket || 01&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Oil filter || 01&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | Anti-vibration spring || 01&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Fuse 2A || 01&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Sealing cord || 02&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Rivet and screw M5x20mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | Self-drilling screw M4.8x32mm || 04&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&lt;br /&gt;
=== Technical specifications of the LIGO-SP product ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L),mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0…9V), Frequency (500… 2000Hz). || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || 9600 || colspan=&amp;quot;2&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V) || 15-37 || 12-37 || 12-37&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP67&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level,% || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the minimum level measurement value || Analog (0...8V); Frequency (500...1500 Hz) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the maximum level measurement value || Analog (1...9V); Frequency (1000...2000 Hz) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, (s) || colspan=&amp;quot;3&amp;quot; | 0...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, (s) || Continuous || 1...60 || 1...60&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±2&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, (years)) || colspan=&amp;quot;3&amp;quot; | 8&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Technical specifications of the LIGO-SP PRO product ===&lt;br /&gt;
&lt;br /&gt;
* LIGO-SP PRO product is designed for devices having wide input voltage (7-75V), the voltage of the internal sensing printed circuit board isolated from outside is 2500V. The internal temperature sensor has a very low error at &amp;lt;0.6°С. The product is designed to operate in extreme environment.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L),mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0…9), Frequency (500… 2000Hz). || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || 9600 || colspan=&amp;quot;2&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V)&amp;lt;br&amp;gt;2500V internal isolated || colspan=&amp;quot;3&amp;quot; | 7-75&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP67&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level,% || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the minimum level measurement value. || Analog (0...8V); Frequency (500...1500 Hz) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Digital reading range corresponding to the maximum level measurement value || Analog (1...9V); Frequency (1000...2000 Hz) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling time, (s) || colspan=&amp;quot;3&amp;quot; | 0...255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, (s) || continuous || 1...60 || 1...60&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±0.6&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, (years) || colspan=&amp;quot;3&amp;quot; | 10&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Selecting the location to install sensor ===&lt;br /&gt;
&lt;br /&gt;
* Installation location should be as close as possible to the geometric center and placed at the deepest level of the tank.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig1-location.png|frameless|894px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Selecting the location to install sensor&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When being installed, the sensor should not be in contact with reinforcement ribs inside the tank&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig2-two-sensors.png|frameless|730px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Installation of two sensors in one tank allows for significant reduction&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Drilling the hole on fuel tank ===&lt;br /&gt;
&lt;br /&gt;
* Drill out the central bore by bimetal core drill ø38 mm&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig3-drilling.png|frameless|580px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Drilling the selected hole&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Drill out four mounting holes according to the diagram:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig4-mounting-holes.png|frameless|541px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Drill out four mounting holes&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mounting hole diameter depends on the tank material:&lt;br /&gt;
&lt;br /&gt;
* ø 4 mm – for metal tank with wall thickness over 3 mm (cut M5 self screw M4.8)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig5-hole-thick.png|frameless|402px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. Hole thick&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ø 7 mm – for plastic and metal tank with wall thickness up to 3 mm (for rivets)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig6-rivet-nut.png|frameless|372px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 6. Rivet nut M5&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig7-install-rivet.png|frameless|535px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 7. Installing revit nut if tank&#039;s wall is thinner than 3mm&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Measuaring the tank depth and cutting sensor&#039;measuring probe ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig8-measure-depth.png|frameless|615px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 8. Measuring the tank depth&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Cut the sensor&#039;s measuring probe so that its length is 25 mm less than the depth of the tank. Ensure that shearing line is perpendicular to the sensor longitudinal axis&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig9-length.png|frameless|723px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 9. Origin length (L) and length after cutting (Lm)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes: Minimum allowable length of the measuring probe is 150 mm.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== On mobile platform ===&lt;br /&gt;
&lt;br /&gt;
Connecting the sensor to LIGO mobile configurator&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-mobile-connect.png|frameless|600px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 9. Connecting the sensor to the LIGO mobile configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Configuring and managing the appropriate settings for the LiGO device sensor is essential to ensure its proper, stable, and accurate operation. It also allows for continuous monitoring of the device&#039;s operational status, potential failures (whether caused by the sensor itself or external factors), and overall lifespan.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-mobile-ui.png|frameless|444px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 10. Device&#039;s management, setting up and configuration software interface&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== On PC Device ===&lt;br /&gt;
==== Installing software and connecting configuration tool ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Preparation:&#039;&#039;&#039;&lt;br /&gt;
* Laptop or PC&lt;br /&gt;
* Power supply 12 -24VDC or battery 12V&lt;br /&gt;
* USB to RS232 cable.&lt;br /&gt;
* Configuration tool from SOJI ELECTRONICS (We recommend to upgrage to the latest possible version)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig10-connect.png|frameless|969px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 10. Connecting USB to RS232 cable and configuration tool to laptop or PC&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
12-24VDC&lt;br /&gt;
&lt;br /&gt;
==== Download and install software. Downloading and installing softwares ====&lt;br /&gt;
&lt;br /&gt;
===== Installing USB to RS232 driver =====&lt;br /&gt;
&lt;br /&gt;
* Installing driver for USB to RS232 cable&lt;br /&gt;
* After finishing right click and check to ensure the driver is installed successful &amp;quot;Computer&amp;quot; &amp;gt;&amp;gt; &amp;quot;Manager&amp;quot; &amp;gt;&amp;gt; &amp;quot;Device Manager&amp;quot; &amp;gt;&amp;gt; &amp;quot;Port (COM &amp;amp; LPT)&amp;quot; as follow the picture bellow, otherwise, please check the cable and cable driver.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig11-driver.png|frameless|678px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 11. Installing USB to RS232 driver successful&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== Installing sensor software configuration =====&lt;br /&gt;
&lt;br /&gt;
* Visit our website: [http://sojielectronics.com/support/download/ http://sojielectronics.com/support/download/] and download setup file &amp;quot;Setup Utility Program &amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig12-config-tool.png|frameless|710px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 12. Sensor configuration tool&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Parameters descriptions:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Load config:&#039;&#039;&#039; Load configurations from sensor to PC. Note: user must load configurations from sensor before changing configurations on PC.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Save config:&#039;&#039;&#039; Save configurations from PC to sensor&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Update firmware:&#039;&#039;&#039; Upgrade new firmware for sensor (visit www.sojielectronics.com for the latest firmware version)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Set full:&#039;&#039;&#039; Set Full for maximum fuel level calibration&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Set Empty:&#039;&#039;&#039; Set Empty for minimum fuel level calibration&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Level Min:&#039;&#039;&#039; Configure LIGO SP-AF output voltage or frequency range according to the voltage or frequency range of the tracking device input.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Level Max:&#039;&#039;&#039; Configure LIGO SP-AF output voltage or frequency range according to the voltage or frequency range of the tracking device input&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Output type:&#039;&#039;&#039; Selecting voltage or frequency output (used only for AF sensor)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Filter time:&#039;&#039;&#039; Setting output signal processing time (0-255). Default is 60 seconds.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Automatic transmission mode:&#039;&#039;&#039; Automatic transmission mode applied only for RS232/RS485 defines sensor output message type:&lt;br /&gt;
* Off – no automatic message transmission, sensor waits for tracking device request;&lt;br /&gt;
* HEX - automatic message transmission in binary format (used by default);&lt;br /&gt;
* ASCII - automatic message transmission in text format;&lt;br /&gt;
* ASCII EXT – automatic message transmission in extended text format. Additional Prefix and Postfix configurable parameters are available for this mode to insert required header or ending of the message.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Message interval:&#039;&#039;&#039; Time period the sensor automatically send output message to the tracking device. Parameter value range is 1…255 seconds with 1 second step. Default value is 1 second.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actual length:&#039;&#039;&#039; The actual length of sensor probe&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Address (0-254):&#039;&#039;&#039; Set the network address for the sensor. When several sensors are connected to one external device, they should have a unique network address.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Parameter selection:&#039;&#039;&#039; Selection of output value type for sensor data&lt;br /&gt;
&lt;br /&gt;
One of the following output value types available for RS232 and RS485 sensor&lt;br /&gt;
* Fuel level in standard (normalized) units (0...1000).&lt;br /&gt;
* Fuel level in millimeters (mm), 0.1 mm step.&lt;br /&gt;
* Fuel volume in liters (L), 0.1 L step.&lt;br /&gt;
* Fuel volume in percentage (%), 0.4% step.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Output:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;OSC frequency:&#039;&#039;&#039; Initial measuring generator frequency (Hz)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Data output:&#039;&#039;&#039; Data output (0-4095)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sensor message:&#039;&#039;&#039; Sensor working message if sensor message is none sensor works fine, otherwise malfunction codes are displayed as table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sensor message !! Transcript of the malfunction Code !! Possible solution&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 255 or 254 || Calibration error || Check if the measuring probe actual size value is inserted correctly and (or) re-calibrate the sensor&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 253 || Short circuit in measuring probe tubes || Wash the measuring probe tubes with clean fuel, clean fuel tank of mud and water.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 252 || Calibration error || Check if the measuring probe actual size value is inserted correctly and (or) re-calibrate the sensor&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 251 || Hardware failure || Contact supplier&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 250 || Calibration error || Check if the measuring probe actual size value is inserted correctly and (or) re-calibrate the sensor.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Calibration Table:&#039;&#039;&#039; This entry is used to record fuel tank calibration into the sensor internal memory. The table is filled out with the data achieved during fuel tank calibration procedure. The data is entered as a table of correspondence between measured fuel level value (Fuel level (uint) field) and fuel volume in the tank (Volume (L) field). Recommended number of table entries is 15. Max possible is 30 entries.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Enable AutoClib feature:&#039;&#039;&#039; Sensor automatically calibrates Min, Max levels after cutting. User does not need to reconfigure after cutting.&lt;br /&gt;
&lt;br /&gt;
===== Min/Max Calibration =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;a) Set Empty level&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Keeping sensor apart from fuel and set Empty level&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig13-set-empty.png|frameless|689px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 13. Setting Empty level.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;b) Set Full level&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Fill the measuring container with fuel.&lt;br /&gt;
* Immerse the sensor in the fuel to the full length of the measuring probe and set Full level&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig14-set-full.png|frameless|655px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 14. Setting Full level&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;c) Installing screen filter&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Screen filter is mounted on the measuring probe of sensor to protect measuring probe electrodes from mud and water. Using the filter extends significantly faultless lifetime of the sensor.&lt;br /&gt;
&lt;br /&gt;
Screen filter mounting order: firstly put on fixator, then put on bottom stop and fix it with two side screws. Put the screen filter over bottom stop and fasten it with a fixator locks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig15-screen-filter.png|frameless|747px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 15. Installing screen filter procedue&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;d) Installing and connecting&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Applying oil-and-petrol resistant non-conductive sealant to ruber gasket&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig16-sealant.png|frameless|595px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 16. Applying the sealant to gasket&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Put the sensor into the tank and fix: when fixing with rivets, use a rivet driver when bolting, put on a seal (per bolt), a spacer and a spring washer when fixing to plastic tanks with wall thickness over 3 mm, use vendor furnished self-tapping screws and a seal (per self-tapping screw)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig17-install.png|frameless|663px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 17. Put sensor into the tank&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Connect the sensors to an external device using 7m cable&lt;br /&gt;
&lt;br /&gt;
The wire cable color:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RS232/RS485:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:RS232-RS485.jpg|frameless|756x756px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 18. Wiring diagram of RS232 and RS485 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | TXD/B-&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | RXD/A+&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 12-37 VDC or 7.5-75V for SP PRO series&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AF:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig19-wiring-AF.png|frameless|873px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 19. Wiring diagram of Analog and Frequency output signals.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | Out (Analog/Frequency)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 15-37 VDC or 7.5-75V for SP PRO series&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Connect the fuse holder to LIGO sensor power cable (brown wire) in close vicinity to the vehicle power supply circuit&lt;br /&gt;
* Installing the fuse in the fuse holder&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;e) Calibration&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Launch Sensor &amp;gt;&amp;gt; load Config &amp;gt;&amp;gt; From Sensor &amp;gt;&amp;gt; Calibration Table&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig20-calib-table.png|frameless|727px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 20. Calibration table&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Pump fuel in the tank, the amount of pumped fuel depends on the tank&#039;s volume (at least 20 times of pumping)&lt;br /&gt;
* After pumping, wait for a while (about 2 minutes) for the output data to be stable, then select &amp;quot;add a row&amp;quot; and insert the actual pumped liters in &amp;quot;liter&amp;quot; box, input the data given by sensor into &amp;quot;Data&amp;quot; box. User can click on the button to sync up data, or read data in the &amp;quot;output&amp;quot; tab as the image below.&lt;br /&gt;
* Repeat the above steps until the oil tank is full (Attention: It is recommended to write down the information on the paper to compare with input data and avoid mistaking during the process)&lt;br /&gt;
* After completing the above process, select &amp;quot;Volume (liter)&#039;&#039; as the output data.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig21-param.png|frameless|464px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 21. Parameter selection&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Finally, click on the &amp;quot;Sensor&amp;quot; button at the top left corner -&amp;gt; Save config -&amp;gt; To sensor to save data for the sensor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention:&#039;&#039;&#039;&lt;br /&gt;
* The time between each pumping must be at least 2 minutes apart or wait for measured values to be stable, then comes the next pumping.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;f) Sealing&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Install the bolt through a hole in the seal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig22-sealing.png|frameless|891px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 22. Sealing&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Quick installation without PC ====&lt;br /&gt;
&lt;br /&gt;
This method is only applicable to configure Min and Max level.&lt;br /&gt;
&lt;br /&gt;
===== Preparation before setting up =====&lt;br /&gt;
&lt;br /&gt;
* The sensor&#039;s auto-recognisation mode needs to be turned on by checking the &amp;quot;Enable AutoCalib Feature&amp;quot; box.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig24-autocalib.png|frameless|865px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 24. Turning on the auto-recognisation mode before installing&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* External devices such as GPS tracking or Dataloger… must have the software integrated with the configurator. The configuration is performed directly on the device.&lt;br /&gt;
&lt;br /&gt;
===== Automatically setting up and configuring the sensor after cutting =====&lt;br /&gt;
&lt;br /&gt;
* After measuring and cutting sensor, remove iron filings and clean burrs between the tubes, then follow steps below:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 1:&#039;&#039;&#039; Do not allow the sensor to contact oil.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 2:&#039;&#039;&#039; Ensure 12-24VDC stable power supply for the sensor.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 3:&#039;&#039;&#039; Make sure the sensor is under stable condition within 30-60s. The sensor will automatically recognise Min and Max level.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Step 4:&#039;&#039;&#039; Cut the power and reboot. The sensor now recognizes new configurations.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig25-auto-config.png|frameless|634px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 25. Leave the sensor in the air, provide with a stable power supply in 30-60s to configure &amp;quot;Min&amp;quot; level.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Attention:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
- In case of improper operation and the sensor cannot recognise properly the actual Min, Max level, user should reconfigure using a PC.&lt;br /&gt;
&lt;br /&gt;
==== Connecting multiple sensors together ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;(Only applicable to RS232 and RS485)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In some situations, there are oil tanks with specific dimensions, interconnected oil tanks, or oil tanks that are either too long or too large. Using just one sensor to measure the accurate fuel level inside these oil tanks may not be feasible. Hence, there is a need to connect two or more sensors together through a data processor called SUM-DATA.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-multi-sensor-tank.png|frameless|728px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 26. Oil tank with particular dimensions connecting multiple sensors together&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Connection diagram of the processor SUM-DATA for multiple sensors:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-sumdata-2sensors.png|frameless|648px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 27: Connecting two sensors by 01 processor SUM-DATA&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-sumdata-multi.png|frameless|707px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 28: Connecting multiple sensors together by multiple processors SUM-DATA&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Operation ==&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
== Maintenance ==&lt;br /&gt;
&lt;br /&gt;
== Certification / Compliance ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[LIGO PRO Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Channel !! Detail&lt;br /&gt;
|-&lt;br /&gt;
| Company || SOJI ELECTRONICS.,JSC&lt;br /&gt;
|-&lt;br /&gt;
| Address || C50-LK13, Lot No. 09, New Urban Area, Le Trong Tan street, Duong Noi Ward, Ha Dong District, Hanoi City, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Tel/Fax || +84 24 62 932 369&lt;br /&gt;
|-&lt;br /&gt;
| Email || contact@sojielectronics.com&lt;br /&gt;
|-&lt;br /&gt;
| Website || [http://sojielectronics.com sojielectronics.com]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=File:RS232-RS485.jpg&amp;diff=2086</id>
		<title>File:RS232-RS485.jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=File:RS232-RS485.jpg&amp;diff=2086"/>
		<updated>2026-07-07T07:33:03Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;up image&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=File:LIGO-BLE-ADAPTER-Product-overview.png&amp;diff=2085</id>
		<title>File:LIGO-BLE-ADAPTER-Product-overview.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=File:LIGO-BLE-ADAPTER-Product-overview.png&amp;diff=2085"/>
		<updated>2026-07-07T07:22:17Z</updated>

		<summary type="html">&lt;p&gt;Admin: Admin moved page File:LIGO-BLE-ADAPTER-Product-overview.png to File:LIGO-AIR-ADAPTER-Product-overview.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[File:LIGO-AIR-ADAPTER-Product-overview.png]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=File:LIGO-AIR-ADAPTER-Product-overview.png&amp;diff=2084</id>
		<title>File:LIGO-AIR-ADAPTER-Product-overview.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=File:LIGO-AIR-ADAPTER-Product-overview.png&amp;diff=2084"/>
		<updated>2026-07-07T07:22:17Z</updated>

		<summary type="html">&lt;p&gt;Admin: Admin moved page File:LIGO-BLE-ADAPTER-Product-overview.png to File:LIGO-AIR-ADAPTER-Product-overview.png&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=MediaWiki:Common.js&amp;diff=2083</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=MediaWiki:Common.js&amp;diff=2083"/>
		<updated>2026-07-07T07:18:55Z</updated>

		<summary type="html">&lt;p&gt;Admin: Update&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;(function () {&lt;br /&gt;
    function esc(text) {&lt;br /&gt;
        return String(text || &amp;quot;&amp;quot;).replace(/[&amp;amp;&amp;lt;&amp;gt;&amp;quot;]/g, function (m) {&lt;br /&gt;
            return { &amp;quot;&amp;amp;&amp;quot;: &amp;quot;&amp;amp;amp;&amp;quot;, &amp;quot;&amp;lt;&amp;quot;: &amp;quot;&amp;amp;lt;&amp;quot;, &amp;quot;&amp;gt;&amp;quot;: &amp;quot;&amp;amp;gt;&amp;quot;, &#039;&amp;quot;&#039;: &amp;quot;&amp;amp;quot;&amp;quot; }[m];&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var ICONS = {&lt;br /&gt;
        location: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 010-5 2.5 2.5 0 010 5z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        phone: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M12 1a9 9 0 00-9 9v7a3 3 0 003 3h3v-8H5v-2a7 7 0 0114 0v2h-4v8h3a3 3 0 003-3v-7a9 9 0 00-9-9z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        email: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;none&amp;quot; stroke=&amp;quot;currentColor&amp;quot; stroke-width=&amp;quot;2&amp;quot; stroke-linecap=&amp;quot;round&amp;quot; stroke-linejoin=&amp;quot;round&amp;quot;&amp;gt;&amp;lt;circle cx=&amp;quot;12&amp;quot; cy=&amp;quot;12&amp;quot; r=&amp;quot;4&amp;quot;/&amp;gt;&amp;lt;path d=&amp;quot;M16 8v5a3 3 0 006 0v-1a10 10 0 10-4 8&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        clock: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;none&amp;quot; stroke=&amp;quot;currentColor&amp;quot; stroke-width=&amp;quot;2&amp;quot; stroke-linecap=&amp;quot;round&amp;quot; stroke-linejoin=&amp;quot;round&amp;quot;&amp;gt;&amp;lt;circle cx=&amp;quot;12&amp;quot; cy=&amp;quot;12&amp;quot; r=&amp;quot;10&amp;quot;/&amp;gt;&amp;lt;polyline points=&amp;quot;12 6 12 12 16 14&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        facebook: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M22 12a10 10 0 10-11.56 9.88v-6.99H7.9V12h2.54V9.8c0-2.51 1.49-3.89 3.77-3.89 1.09 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56V12h2.77l-.44 2.89h-2.33v6.99A10 10 0 0022 12z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        whatsapp: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M17.5 14.4c-.3-.1-1.7-.8-1.9-.9-.3-.1-.5-.1-.7.1-.2.3-.7.9-.9 1.1-.2.2-.3.2-.6.1-.3-.1-1.2-.4-2.3-1.4-.9-.8-1.4-1.7-1.6-2-.2-.3 0-.5.1-.6.1-.1.3-.3.4-.5.1-.2.2-.3.3-.5.1-.2 0-.4 0-.5 0-.1-.7-1.7-1-2.3-.3-.6-.5-.5-.7-.5h-.6c-.2 0-.5.1-.8.4-.3.3-1 1-1 2.5s1.1 2.9 1.2 3.1c.1.2 2.1 3.2 5.1 4.5.7.3 1.3.5 1.7.6.7.2 1.4.2 1.9.1.6-.1 1.7-.7 2-1.4.2-.7.2-1.3.2-1.4-.1-.1-.3-.2-.5-.3zM12 2a10 10 0 00-8.5 15.3L2 22l4.8-1.4A10 10 0 1012 2z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        linkedin: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M19 3H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2zM8.3 18.3H5.7V9.7h2.6v8.6zM7 8.5a1.5 1.5 0 110-3 1.5 1.5 0 010 3zm11.3 9.8h-2.6v-4.2c0-1 0-2.3-1.4-2.3s-1.6 1.1-1.6 2.2v4.3h-2.6V9.7h2.5v1.2a2.7 2.7 0 012.5-1.4c2.6 0 3.1 1.7 3.1 4v4.8z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        youtube: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M23.5 6.2a3 3 0 00-2.1-2.1C19.5 3.5 12 3.5 12 3.5s-7.5 0-9.4.6A3 3 0 00.5 6.2 31.3 31.3 0 000 12a31.3 31.3 0 00.5 5.8 3 3 0 002.1 2.1c1.9.6 9.4.6 9.4.6s7.5 0 9.4-.6a3 3 0 002.1-2.1A31.3 31.3 0 0024 12a31.3 31.3 0 00-.5-5.8zM9.6 15.6V8.4l6.3 3.6-6.3 3.6z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    function getIcon(name) {&lt;br /&gt;
        return &#039;&amp;lt;span class=&amp;quot;soji-footer-icon&amp;quot;&amp;gt;&#039; + (ICONS[name] || &amp;quot;&amp;quot;) + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function renderLink(item, extraClass) {&lt;br /&gt;
    var cls = extraClass ? &#039; class=&amp;quot;&#039; + extraClass + &#039;&amp;quot;&#039; : &amp;quot;&amp;quot;;&lt;br /&gt;
    var href = item.href ? esc(item.href) : &amp;quot;#&amp;quot;;&lt;br /&gt;
    return &#039;&amp;lt;a&#039; + cls + &#039; href=&amp;quot;&#039; + href + &#039;&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;&#039; + esc(item.label) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    function buildCompany(company) {&lt;br /&gt;
        if (!company) return &amp;quot;&amp;quot;;&lt;br /&gt;
        var contacts = (company.contacts || []).map(function (c) {&lt;br /&gt;
            return &#039;&amp;lt;li class=&amp;quot;soji-footer-contact&amp;quot;&amp;gt;&#039; + getIcon(c.icon) +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + esc(c.text) + &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
        }).join(&amp;quot;&amp;quot;);&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;&amp;lt;section class=&amp;quot;soji-footer-company&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;h2 class=&amp;quot;soji-footer-company-name&amp;quot;&amp;gt;&#039; + esc(company.name) + &amp;quot;&amp;lt;/h2&amp;gt;&amp;quot;,&lt;br /&gt;
            &#039;&amp;lt;ul class=&amp;quot;soji-footer-contact-list&amp;quot;&amp;gt;&#039; + contacts + &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
        ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildColumns(columns) {&lt;br /&gt;
        return (columns || []).map(function (col) {&lt;br /&gt;
            var items = (col.items || []).map(function (item) {&lt;br /&gt;
                return &amp;quot;&amp;lt;li&amp;gt;&amp;quot; + renderLink(item, &amp;quot;soji-footer-link&amp;quot;) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
            }).join(&amp;quot;&amp;quot;);&lt;br /&gt;
            return [&lt;br /&gt;
                &#039;&amp;lt;section class=&amp;quot;soji-footer-col&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
                &amp;quot;&amp;lt;h2&amp;gt;&amp;quot; + esc(col.title) + &amp;quot;&amp;lt;/h2&amp;gt;&amp;quot;,&lt;br /&gt;
                &amp;quot;&amp;lt;ul&amp;gt;&amp;quot; + items + &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;,&lt;br /&gt;
                &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
            ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
        }).join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildSubscribe(data) {&lt;br /&gt;
        var sub = data.subscribe || {};&lt;br /&gt;
        var social = (data.social || []).map(function (item) {&lt;br /&gt;
    var href = item.href ? esc(item.href) : &amp;quot;#&amp;quot;;&lt;br /&gt;
    var icon = item.icon&lt;br /&gt;
        ? getIcon(item.icon)&lt;br /&gt;
        : esc(item.short || item.label.charAt(0));&lt;br /&gt;
    return &#039;&amp;lt;a class=&amp;quot;soji-footer-social-item&amp;quot; href=&amp;quot;&#039; + href + &#039;&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot; aria-label=&amp;quot;&#039; + esc(item.label) + &#039;&amp;quot;&amp;gt;&#039; + icon + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
}).join(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;&amp;lt;section class=&amp;quot;soji-footer-subscribe-col&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &amp;quot;&amp;lt;h2&amp;gt;&amp;quot; + esc(sub.title || &amp;quot;Subscribe&amp;quot;) + &amp;quot;&amp;lt;/h2&amp;gt;&amp;quot;,&lt;br /&gt;
            &#039;&amp;lt;form class=&amp;quot;soji-footer-subscribe-form&amp;quot; action=&amp;quot;&#039; + esc(sub.action || &amp;quot;#&amp;quot;) + &#039;&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;input type=&amp;quot;email&amp;quot; name=&amp;quot;email&amp;quot; class=&amp;quot;soji-footer-subscribe-input&amp;quot; placeholder=&amp;quot;&#039; + esc(sub.placeholder || &amp;quot;Email Address *&amp;quot;) + &#039;&amp;quot; required&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;submit&amp;quot; class=&amp;quot;soji-footer-subscribe-btn&amp;quot;&amp;gt;&#039; + esc(sub.buttonLabel || &amp;quot;Send&amp;quot;) + &amp;quot;&amp;lt;/button&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;,&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;soji-footer-social&amp;quot;&amp;gt;&#039; + social + &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
        ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildFooter(data) {&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;&amp;lt;section id=&amp;quot;soji-site-footer&amp;quot; class=&amp;quot;soji-site-footer&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;soji-footer-shell&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;soji-footer-grid&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            buildCompany(data.company),&lt;br /&gt;
            buildColumns(data.columns),&lt;br /&gt;
            buildSubscribe(data),&lt;br /&gt;
            &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
            data.copyright ? &#039;&amp;lt;div class=&amp;quot;soji-footer-copy&amp;quot;&amp;gt;&#039; + esc(data.copyright).replace(/\{\{YEAR\}\}/g, new Date().getFullYear()) + &amp;quot;&amp;lt;/div&amp;gt;&amp;quot; : &amp;quot;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
        ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function injectFooter(data) {&lt;br /&gt;
    if (document.getElementById(&amp;quot;soji-site-footer&amp;quot;)) return;&lt;br /&gt;
    document.body.insertAdjacentHTML(&amp;quot;beforeend&amp;quot;, buildFooter(data));&lt;br /&gt;
    document.body.classList.add(&amp;quot;has-soji-footer&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        fetch(&amp;quot;/index.php?title=MediaWiki:FooterData.json&amp;amp;action=raw&amp;quot;)&lt;br /&gt;
            .then(function (r) { return r.text(); })&lt;br /&gt;
            .then(function (text) { return JSON.parse(text); })&lt;br /&gt;
            .then(injectFooter)&lt;br /&gt;
            .catch(function (err) { console.error(&amp;quot;Footer load failed:&amp;quot;, err); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &amp;quot;loading&amp;quot;) {&lt;br /&gt;
        document.addEventListener(&amp;quot;DOMContentLoaded&amp;quot;, init);&lt;br /&gt;
    } else {&lt;br /&gt;
        init();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    if (mw.config.get(&#039;wgNamespaceNumber&#039;) &amp;lt; 0) return;&lt;br /&gt;
&lt;br /&gt;
    var title = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
    var pageName = mw.config.get(&#039;wgPageName&#039;).replace(/_/g, &#039; &#039;);&lt;br /&gt;
&lt;br /&gt;
    // Page Main Page: không cần breadcrumb&lt;br /&gt;
    if (pageName === &#039;Main Page&#039;) return;&lt;br /&gt;
&lt;br /&gt;
    // Lấy category trực tiếp của page hoặc của category page hiện tại&lt;br /&gt;
    var initialCats = mw.config.get(&#039;wgCategories&#039;) || [];&lt;br /&gt;
    if (!initialCats.length) return;&lt;br /&gt;
&lt;br /&gt;
    var api = new mw.Api();&lt;br /&gt;
    var chain = [];&lt;br /&gt;
    var seen = {};&lt;br /&gt;
&lt;br /&gt;
    // Đi ngược lên cây qua API&lt;br /&gt;
    function walkUp(catName) {&lt;br /&gt;
        if (!catName || seen[catName] || catName === &#039;Main Page&#039;) {&lt;br /&gt;
            return $.Deferred().resolve();&lt;br /&gt;
        }&lt;br /&gt;
        seen[catName] = true;&lt;br /&gt;
        chain.unshift(catName);&lt;br /&gt;
&lt;br /&gt;
        return api.get({&lt;br /&gt;
            action: &#039;query&#039;,&lt;br /&gt;
            titles: &#039;Category:&#039; + catName,&lt;br /&gt;
            prop: &#039;categories&#039;,&lt;br /&gt;
            cllimit: 1,&lt;br /&gt;
            format: &#039;json&#039;,&lt;br /&gt;
            formatversion: 2&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            if (!pages.length || !pages[0].categories) return;&lt;br /&gt;
            var parent = pages[0].categories[0].title.replace(/^Category:/, &#039;&#039;);&lt;br /&gt;
            return walkUp(parent);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    walkUp(initialCats[0]).then(function () {&lt;br /&gt;
        var parts = [&lt;br /&gt;
            &#039;&amp;lt;a href=&amp;quot;&#039; + mw.util.getUrl(&#039;Main Page&#039;) + &#039;&amp;quot;&amp;gt;Main Page&amp;lt;/a&amp;gt;&#039;&lt;br /&gt;
        ];&lt;br /&gt;
        chain.forEach(function (c) {&lt;br /&gt;
            parts.push(&#039;&amp;lt;a href=&amp;quot;&#039; + mw.util.getUrl(c) + &#039;&amp;quot;&amp;gt;&#039; + c + &#039;&amp;lt;/a&amp;gt;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        parts.push(&#039;&amp;lt;strong&amp;gt;&#039; + title + &#039;&amp;lt;/strong&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var html = &#039;&amp;lt;nav class=&amp;quot;custom-breadcrumb&amp;quot;&amp;gt;&#039; + parts.join(&#039; &amp;amp;gt; &#039;) + &#039;&amp;lt;/nav&amp;gt;&#039;;&lt;br /&gt;
        $(&#039;#mw-content-text&#039;).before(html);&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Tự động thêm target=&amp;quot;_blank&amp;quot; cho mọi link tới file .pdf&lt;br /&gt;
    $(&#039;a[href$=&amp;quot;.pdf&amp;quot;], a[href*=&amp;quot;.pdf?&amp;quot;], a[href*=&amp;quot;/file/&amp;quot;][href*=&amp;quot;.pdf&amp;quot;]&#039;).each(function () {&lt;br /&gt;
        $(this).attr(&#039;target&#039;, &#039;_blank&#039;);&lt;br /&gt;
        $(this).attr(&#039;rel&#039;, &#039;noopener noreferrer&#039;);&lt;br /&gt;
        // Thêm icon PDF nhỏ&lt;br /&gt;
        if (!$(this).find(&#039;.pdf-icon&#039;).length) {&lt;br /&gt;
            $(this).append(&#039; &amp;lt;span class=&amp;quot;pdf-icon&amp;quot; title=&amp;quot;Open PDF in new tab&amp;quot;&amp;gt;📄&amp;lt;/span&amp;gt;&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Force &amp;quot;View&amp;quot; links to open in new tab&lt;br /&gt;
    $(&#039;.doc-view a&#039;).attr({&lt;br /&gt;
        target: &#039;_blank&#039;,&lt;br /&gt;
        rel: &#039;noopener noreferrer&#039;&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Force &amp;quot;Download&amp;quot; links to trigger file download&lt;br /&gt;
    $(&#039;.doc-download a&#039;).each(function () {&lt;br /&gt;
        var $a = $(this);&lt;br /&gt;
        var href = $a.attr(&#039;href&#039;);&lt;br /&gt;
        // Extract filename from URL&lt;br /&gt;
        var match = href.match(/\/([^\/]+\.(?:pdf|zip|doc|docx|xls|xlsx))$/i);&lt;br /&gt;
        if (match) {&lt;br /&gt;
            $a.attr(&#039;download&#039;, match[1]);&lt;br /&gt;
        } else {&lt;br /&gt;
            $a.attr(&#039;download&#039;, &#039;&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================&lt;br /&gt;
 * Auto-collapse TOC sub-headings on page load&lt;br /&gt;
 * Only show H2 (main Heading) by default&lt;br /&gt;
 * ============================================ */&lt;br /&gt;
$(function () {&lt;br /&gt;
    var attempts = 0;&lt;br /&gt;
    var maxAttempts = 15;&lt;br /&gt;
&lt;br /&gt;
    function collapseAllTOC() {&lt;br /&gt;
        // Find all expanded toggle buttons in TOC&lt;br /&gt;
        var $toggles = $(&#039;.vector-toc-toggle[aria-expanded=&amp;quot;true&amp;quot;]&#039;);&lt;br /&gt;
        &lt;br /&gt;
        if ($toggles.length &amp;gt; 0) {&lt;br /&gt;
            $toggles.each(function () {&lt;br /&gt;
                // Click toggle to collapse (triggers Vue.js handler)&lt;br /&gt;
                this.click();&lt;br /&gt;
            });&lt;br /&gt;
        } else if (attempts &amp;lt; maxAttempts) {&lt;br /&gt;
            // TOC chưa render xong, retry&lt;br /&gt;
            attempts++;&lt;br /&gt;
            setTimeout(collapseAllTOC, 100);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    collapseAllTOC();&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================&lt;br /&gt;
 * Sync chevron arrow with collapsible state&lt;br /&gt;
 * ============================================ */&lt;br /&gt;
$(function () {&lt;br /&gt;
    $(&#039;.category-block-toggle span[class*=&amp;quot;mw-customtoggle-cb-&amp;quot;]&#039;).each(function () {&lt;br /&gt;
        var $toggle = $(this);&lt;br /&gt;
        var match = $toggle.attr(&#039;class&#039;).match(/mw-customtoggle-(\S+)/);&lt;br /&gt;
        if (!match) return;&lt;br /&gt;
&lt;br /&gt;
        var $target = $(&#039;#mw-customcollapsible-&#039; + match[1]);&lt;br /&gt;
        if (!$target.length) return;&lt;br /&gt;
&lt;br /&gt;
        function syncArrow() {&lt;br /&gt;
            if ($target.hasClass(&#039;mw-collapsed&#039;)) {&lt;br /&gt;
                $toggle.removeClass(&#039;is-expanded&#039;);&lt;br /&gt;
            } else {&lt;br /&gt;
                $toggle.addClass(&#039;is-expanded&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Initial state&lt;br /&gt;
        syncArrow();&lt;br /&gt;
&lt;br /&gt;
        // Update on click&lt;br /&gt;
        $toggle.on(&#039;click&#039;, function () {&lt;br /&gt;
            setTimeout(syncArrow, 50);&lt;br /&gt;
        });&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================&lt;br /&gt;
 * Pending Changes Badge for Reviewers&lt;br /&gt;
 * Show notification of pending changes count&lt;br /&gt;
 * ============================================ */&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Chỉ show cho user có quyền review (reviewer, sysop, admin)&lt;br /&gt;
    var userGroups = mw.config.get(&#039;wgUserGroups&#039;) || [];&lt;br /&gt;
    var hasReviewRights = userGroups.some(function (g) {&lt;br /&gt;
        return [&#039;reviewer&#039;, &#039;sysop&#039;, &#039;bureaucrat&#039;].indexOf(g) !== -1;&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (!hasReviewRights) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetch pending count from API&lt;br /&gt;
    function fetchPendingCount() {&lt;br /&gt;
        new mw.Api().get({&lt;br /&gt;
            action: &#039;query&#039;,&lt;br /&gt;
            list: &#039;oldreviewedpages&#039;,&lt;br /&gt;
            ornamespace: &#039;0|14&#039;,&lt;br /&gt;
            orfilterredir: &#039;nonredirects&#039;,&lt;br /&gt;
            orlimit: 500,&lt;br /&gt;
            format: &#039;json&#039;&lt;br /&gt;
        }).done(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.oldreviewedpages) || [];&lt;br /&gt;
            updateBadge(pages.length);&lt;br /&gt;
        }).fail(function (err) {&lt;br /&gt;
            console.log(&#039;Failed to fetch pending changes:&#039;, err);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Update or create badge in sidebar&lt;br /&gt;
    function updateBadge(count) {&lt;br /&gt;
        var $badge = $(&#039;#pending-reviews-badge&#039;);&lt;br /&gt;
&lt;br /&gt;
        if (!$badge.length) {&lt;br /&gt;
            // Tạo badge mới — tìm sidebar và prepend&lt;br /&gt;
            var badgeHtml =&lt;br /&gt;
                &#039;&amp;lt;div id=&amp;quot;pending-reviews-badge&amp;quot; style=&amp;quot;&#039; +&lt;br /&gt;
                &#039;padding: 12px 16px;&#039; +&lt;br /&gt;
                &#039;margin: 12px 8px;&#039; +&lt;br /&gt;
                &#039;background: #fff3e0;&#039; +&lt;br /&gt;
                &#039;border: 1px solid #f57c00;&#039; +&lt;br /&gt;
                &#039;border-left: 4px solid #e65100;&#039; +&lt;br /&gt;
                &#039;border-radius: 4px;&#039; +&lt;br /&gt;
                &#039;cursor: pointer;&#039; +&lt;br /&gt;
                &#039;transition: background 0.2s;&#039; +&lt;br /&gt;
                &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;a href=&amp;quot;&#039; + mw.util.getUrl(&#039;Special:PendingChanges&#039;) + &#039;&amp;quot; style=&amp;quot;&#039; +&lt;br /&gt;
                &#039;color: #e65100;&#039; +&lt;br /&gt;
                &#039;text-decoration: none;&#039; +&lt;br /&gt;
                &#039;font-weight: 600;&#039; +&lt;br /&gt;
                &#039;font-size: 0.95em;&#039; +&lt;br /&gt;
                &#039;display: block;&#039; +&lt;br /&gt;
                &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;⚠️ &amp;lt;span id=&amp;quot;pending-count&amp;quot; style=&amp;quot;&#039; +&lt;br /&gt;
                &#039;display: inline-block;&#039; +&lt;br /&gt;
                &#039;background: #e65100;&#039; +&lt;br /&gt;
                &#039;color: white;&#039; +&lt;br /&gt;
                &#039;padding: 2px 10px;&#039; +&lt;br /&gt;
                &#039;border-radius: 12px;&#039; +&lt;br /&gt;
                &#039;margin: 0 6px;&#039; +&lt;br /&gt;
                &#039;font-weight: 700;&#039; +&lt;br /&gt;
                &#039;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;pending review&#039; +&lt;br /&gt;
                &#039;&amp;lt;/a&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
            // Try multiple sidebar selectors (Vector 2022 + legacy)&lt;br /&gt;
            var $sidebar = $(&#039;#mw-panel .vector-main-menu-content&#039;)&lt;br /&gt;
                .add(&#039;#mw-panel-toc-list&#039;)&lt;br /&gt;
                .add(&#039;#mw-panel&#039;)&lt;br /&gt;
                .add(&#039;.vector-main-menu&#039;)&lt;br /&gt;
                .first();&lt;br /&gt;
&lt;br /&gt;
            if ($sidebar.length) {&lt;br /&gt;
                $sidebar.prepend(badgeHtml);&lt;br /&gt;
                $badge = $(&#039;#pending-reviews-badge&#039;);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Fallback: insert at top of body&lt;br /&gt;
                $(&#039;body&#039;).prepend(&lt;br /&gt;
                    &#039;&amp;lt;div style=&amp;quot;position:fixed;top:60px;right:20px;z-index:9999;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                    badgeHtml + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                );&lt;br /&gt;
                $badge = $(&#039;#pending-reviews-badge&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $(&#039;#pending-count&#039;).text(count);&lt;br /&gt;
&lt;br /&gt;
        if (count === 0) {&lt;br /&gt;
            $badge.hide();&lt;br /&gt;
        } else {&lt;br /&gt;
            $badge.show();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Initial fetch&lt;br /&gt;
    fetchPendingCount();&lt;br /&gt;
&lt;br /&gt;
    // Refresh every 60 seconds&lt;br /&gt;
    setInterval(fetchPendingCount, 60000);&lt;br /&gt;
&lt;br /&gt;
    // Also refresh on focus (when user comes back to tab)&lt;br /&gt;
    $(window).on(&#039;focus&#039;, fetchPendingCount);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Placeholder auto-hide&lt;br /&gt;
 *  Logic: section nào có user content → ẩn placeholder trong section đó&lt;br /&gt;
 *         section trống chỉ có placeholder → vẫn hiển thị (làm hint)&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function hidePlaceholdersInFilledSections() {&lt;br /&gt;
        var $headings = $( &#039;.mw-parser-output h1, .mw-parser-output h2&#039; );&lt;br /&gt;
        if ( !$headings.length ) return;&lt;br /&gt;
&lt;br /&gt;
        $headings.each( function ( i ) {&lt;br /&gt;
            var $h = $( this );&lt;br /&gt;
            var $nextH = $headings.eq( i + 1 );&lt;br /&gt;
            var $sectionEls = $nextH.length ? $h.nextUntil( $nextH ) : $h.nextAll();&lt;br /&gt;
&lt;br /&gt;
            // Đọc text trong section, loại trừ text của placeholder&lt;br /&gt;
            var contentText = &#039;&#039;;&lt;br /&gt;
            $sectionEls.each( function () {&lt;br /&gt;
                var $clone = $( this ).clone();&lt;br /&gt;
                $clone.find( &#039;.soji-placeholder&#039; ).remove();&lt;br /&gt;
                contentText += $clone.text();&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            // Nếu section có user content (ngoài placeholder) → ẩn placeholder&lt;br /&gt;
            if ( contentText.trim().length &amp;gt; 0 ) {&lt;br /&gt;
                $sectionEls.find( &#039;.soji-placeholder&#039; ).hide();&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // Xử lý riêng placeholder trong table cell:&lt;br /&gt;
        // Nếu cell có content khác → ẩn placeholder của cell đó&lt;br /&gt;
        $( &#039;.soji-placeholder:visible&#039; ).each( function () {&lt;br /&gt;
            var $ph = $( this );&lt;br /&gt;
            var $cell = $ph.closest( &#039;td, th&#039; );&lt;br /&gt;
            if ( $cell.length ) {&lt;br /&gt;
                var $clone = $cell.clone();&lt;br /&gt;
                $clone.find( &#039;.soji-placeholder&#039; ).remove();&lt;br /&gt;
                if ( $clone.text().trim().length &amp;gt; 0 ) {&lt;br /&gt;
                    $ph.hide();&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( hidePlaceholdersInFilledSections );&lt;br /&gt;
&lt;br /&gt;
})( jQuery );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Edit summary helper&lt;br /&gt;
 *  Show hint placeholder in edit summary field&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    $( function () {&lt;br /&gt;
        if ( mw.config.get( &#039;wgAction&#039; ) !== &#039;edit&#039; &amp;amp;&amp;amp; mw.config.get( &#039;wgAction&#039; ) !== &#039;submit&#039; ) return;&lt;br /&gt;
&lt;br /&gt;
        var $summary = $( &#039;#wpSummary&#039; );&lt;br /&gt;
        if ( !$summary.length ) return;&lt;br /&gt;
&lt;br /&gt;
        // Add placeholder hint&lt;br /&gt;
        $summary.attr( &#039;placeholder&#039;,&lt;br /&gt;
            &#039;Describe your changes (will appear in Revision History — e.g., &amp;quot;Updated electrical specs&amp;quot;, &amp;quot;Fixed typo in safety section&amp;quot;)&#039;&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        // Add character counter&lt;br /&gt;
        var $counter = $( &#039;&amp;lt;div class=&amp;quot;fw-summary-counter&amp;quot; style=&amp;quot;font-size:0.85em; color:#666; margin-top:4px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039; );&lt;br /&gt;
        $summary.after( $counter );&lt;br /&gt;
&lt;br /&gt;
        function updateCounter() {&lt;br /&gt;
            var len = $summary.val().length;&lt;br /&gt;
            var color = len &amp;lt; 10 ? &#039;#d33&#039; : len &amp;lt; 30 ? &#039;#f80&#039; : &#039;#0a0&#039;;&lt;br /&gt;
            $counter.html(&lt;br /&gt;
                &#039;&amp;lt;span style=&amp;quot;color:&#039; + color + &#039;&amp;quot;&amp;gt;&#039; + len + &#039;&amp;lt;/span&amp;gt; characters &#039; +&lt;br /&gt;
                ( len &amp;lt; 10 ? &#039;(too short — describe what changed)&#039; :&lt;br /&gt;
                  len &amp;lt; 30 ? &#039;(acceptable — be more specific)&#039; :&lt;br /&gt;
                  &#039;(good — descriptive)&#039; )&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $summary.on( &#039;input keyup&#039;, updateCounter );&lt;br /&gt;
        updateCounter();&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Auto Revision History Table&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function formatVersion( n ) {&lt;br /&gt;
        var major = Math.floor( ( n - 1 ) / 10 ) + 1;&lt;br /&gt;
        var minor = ( n - 1 ) % 10;&lt;br /&gt;
        return major + &#039;.&#039; + minor;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function formatDate( isoTimestamp ) {&lt;br /&gt;
        var d = new Date( isoTimestamp );&lt;br /&gt;
        var dd = ( &#039;0&#039; + d.getDate() ).slice( -2 );&lt;br /&gt;
        var mm = ( &#039;0&#039; + ( d.getMonth() + 1 ) ).slice( -2 );&lt;br /&gt;
        return dd + &#039;/&#039; + mm + &#039;/&#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeHtml( str ) {&lt;br /&gt;
        return ( str || &#039;&#039; )&lt;br /&gt;
            .replace( /&amp;amp;/g, &#039;&amp;amp;amp;&#039; )&lt;br /&gt;
            .replace( /&amp;lt;/g, &#039;&amp;amp;lt;&#039; )&lt;br /&gt;
            .replace( /&amp;gt;/g, &#039;&amp;amp;gt;&#039; )&lt;br /&gt;
            .replace( /&amp;quot;/g, &#039;&amp;amp;quot;&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderRevisionHistory() {&lt;br /&gt;
    var $containers = $( &#039;.revision-history-auto&#039; );&lt;br /&gt;
    if ( !$containers.length ) return;&lt;br /&gt;
&lt;br /&gt;
    var pageName = mw.config.get( &#039;wgPageName&#039; );&lt;br /&gt;
&lt;br /&gt;
    new mw.Api().get( {&lt;br /&gt;
        action: &#039;query&#039;,&lt;br /&gt;
        prop: &#039;revisions&#039;,&lt;br /&gt;
        titles: pageName,&lt;br /&gt;
        rvprop: &#039;timestamp|user|comment&#039;,&lt;br /&gt;
        rvlimit: &#039;max&#039;,&lt;br /&gt;
        rvdir: &#039;newer&#039;,           // oldest first&lt;br /&gt;
        formatversion: 2&lt;br /&gt;
    } ).done( function ( data ) {&lt;br /&gt;
        var page = data.query.pages[ 0 ];&lt;br /&gt;
        if ( !page || !page.revisions ) {&lt;br /&gt;
            $containers.html( &#039;&amp;lt;em&amp;gt;No revision history available.&amp;lt;/em&amp;gt;&#039; );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var revisions = page.revisions;&lt;br /&gt;
&lt;br /&gt;
        // Case 1: chỉ có 1 revision = template gen, chưa edit&lt;br /&gt;
        if ( revisions.length &amp;lt; 2 ) {&lt;br /&gt;
            $containers.html(&lt;br /&gt;
                &#039;&amp;lt;em style=&amp;quot;color:#888&amp;quot;&amp;gt;No edits yet. &#039; +&lt;br /&gt;
                &#039;Edit this page to update the content — the first edit will be recorded as v1.0.&amp;lt;/em&amp;gt;&#039;&lt;br /&gt;
            );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Case 2: có &amp;gt;= 2 revisions&lt;br /&gt;
        // - revision 0 = template creation → SKIP&lt;br /&gt;
        // - revision 1 = first edit → v1.0&lt;br /&gt;
        // - revision 2 = second edit → v1.1&lt;br /&gt;
        // - ...&lt;br /&gt;
        var rows = [];&lt;br /&gt;
&lt;br /&gt;
        revisions.forEach( function ( rev, i ) {&lt;br /&gt;
            // Skip first revision (template creation)&lt;br /&gt;
            if ( i === 0 ) return;&lt;br /&gt;
&lt;br /&gt;
            // i=1 → v1.0, i=2 → v1.1, ...&lt;br /&gt;
            var version = formatVersion( i );&lt;br /&gt;
            var date    = formatDate( rev.timestamp );&lt;br /&gt;
            var author  = escapeHtml( rev.user || &#039;unknown&#039; );&lt;br /&gt;
            var desc    = escapeHtml( rev.comment ) || &#039;&amp;lt;em&amp;gt;(no edit summary)&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
            rows.push(&lt;br /&gt;
                &#039;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039; + date + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;td&amp;gt;&amp;lt;strong&amp;gt;v&#039; + version + &#039;&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;td&amp;gt;&#039; + author + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;td&amp;gt;&#039; + desc + &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;&lt;br /&gt;
            );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        // Newest first&lt;br /&gt;
        rows.reverse();&lt;br /&gt;
&lt;br /&gt;
        var html =&lt;br /&gt;
            &#039;&amp;lt;table class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Date&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Version&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th style=&amp;quot;width:20%&amp;quot;&amp;gt;Author&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;tbody&amp;gt;&#039; + rows.join( &#039;&#039; ) + &#039;&amp;lt;/tbody&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/table&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
        $containers.html( html );&lt;br /&gt;
    } ).fail( function ( err ) {&lt;br /&gt;
        $containers.html( &#039;&amp;lt;em style=&amp;quot;color:#d33&amp;quot;&amp;gt;Failed to load revision history.&amp;lt;/em&amp;gt;&#039; );&lt;br /&gt;
    } );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    $( renderRevisionHistory );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Force edit summary in VisualEditor save dialog&lt;br /&gt;
 *  Disable Save button until summary is non-empty (min 5 chars)&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var MIN_LENGTH = 5;&lt;br /&gt;
&lt;br /&gt;
    function setupVEEnforcement() {&lt;br /&gt;
        // Wait for VE save dialog to appear&lt;br /&gt;
        var observer = new MutationObserver( function () {&lt;br /&gt;
            var $dialog = $( &#039;.ve-ui-mwSaveDialog:visible&#039; );&lt;br /&gt;
            if ( !$dialog.length ) return;&lt;br /&gt;
            if ( $dialog.data( &#039;summary-enforced&#039; ) ) return;&lt;br /&gt;
&lt;br /&gt;
            $dialog.data( &#039;summary-enforced&#039;, true );&lt;br /&gt;
&lt;br /&gt;
            var $summaryInput = $dialog.find( &#039;textarea[name=&amp;quot;wpSummary&amp;quot;], input[name=&amp;quot;wpSummary&amp;quot;]&#039; );&lt;br /&gt;
            if ( !$summaryInput.length ) {&lt;br /&gt;
                $summaryInput = $dialog.find( &#039;.ve-ui-mwSaveDialog-summary textarea, .ve-ui-mwSaveDialog-summary input&#039; );&lt;br /&gt;
            }&lt;br /&gt;
            if ( !$summaryInput.length ) return;&lt;br /&gt;
&lt;br /&gt;
            var $saveBtn = $dialog.find( &#039;.oo-ui-flaggedElement-primary button, button.oo-ui-flaggedElement-primary&#039; );&lt;br /&gt;
            if ( !$saveBtn.length ) {&lt;br /&gt;
                $saveBtn = $dialog.find( &#039;a.oo-ui-flaggedElement-primary&#039; );&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Add hint message above summary&lt;br /&gt;
            if ( !$dialog.find( &#039;.soji-summary-hint&#039; ).length ) {&lt;br /&gt;
                $summaryInput.before(&lt;br /&gt;
                    &#039;&amp;lt;div class=&amp;quot;soji-summary-hint&amp;quot; style=&amp;quot;background:#fff3cd; color:#856404; padding:8px 12px; border-left:4px solid #ffc107; margin-bottom:8px; font-size:0.9em; border-radius:3px;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                    &#039;⚠ &amp;lt;strong&amp;gt;Edit summary required.&amp;lt;/strong&amp;gt; Describe what you changed (minimum &#039; + MIN_LENGTH + &#039; characters). Used for Revision History.&#039; +&lt;br /&gt;
                    &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                );&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Add counter&lt;br /&gt;
            if ( !$dialog.find( &#039;.soji-summary-counter&#039; ).length ) {&lt;br /&gt;
                $summaryInput.after(&lt;br /&gt;
                    &#039;&amp;lt;div class=&amp;quot;soji-summary-counter&amp;quot; style=&amp;quot;font-size:0.85em; margin-top:4px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                );&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            function updateState() {&lt;br /&gt;
                var len = $summaryInput.val().trim().length;&lt;br /&gt;
                var $counter = $dialog.find( &#039;.soji-summary-counter&#039; );&lt;br /&gt;
&lt;br /&gt;
                if ( len === 0 ) {&lt;br /&gt;
                    $counter.html( &#039;&amp;lt;span style=&amp;quot;color:#d33&amp;quot;&amp;gt;⚠ Empty — please describe your changes&amp;lt;/span&amp;gt;&#039; );&lt;br /&gt;
                    disableSaveButton( $saveBtn );&lt;br /&gt;
                } else if ( len &amp;lt; MIN_LENGTH ) {&lt;br /&gt;
                    $counter.html( &#039;&amp;lt;span style=&amp;quot;color:#f80&amp;quot;&amp;gt;⚠ Too short (&#039; + len + &#039;/&#039; + MIN_LENGTH + &#039; chars minimum)&amp;lt;/span&amp;gt;&#039; );&lt;br /&gt;
                    disableSaveButton( $saveBtn );&lt;br /&gt;
                } else {&lt;br /&gt;
                    $counter.html( &#039;&amp;lt;span style=&amp;quot;color:#0a0&amp;quot;&amp;gt;✓ &#039; + len + &#039; characters — good&amp;lt;/span&amp;gt;&#039; );&lt;br /&gt;
                    enableSaveButton( $saveBtn );&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            $summaryInput.on( &#039;input keyup change&#039;, updateState );&lt;br /&gt;
            updateState();&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        observer.observe( document.body, { childList: true, subtree: true } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function disableSaveButton( $btn ) {&lt;br /&gt;
        $btn.css( &#039;opacity&#039;, &#039;0.5&#039; ).css( &#039;pointer-events&#039;, &#039;none&#039; );&lt;br /&gt;
        $btn.attr( &#039;aria-disabled&#039;, &#039;true&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function enableSaveButton( $btn ) {&lt;br /&gt;
        $btn.css( &#039;opacity&#039;, &#039;&#039; ).css( &#039;pointer-events&#039;, &#039;&#039; );&lt;br /&gt;
        $btn.removeAttr( &#039;aria-disabled&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( setupVEEnforcement );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Edit Draft Auto-Save (localStorage)&lt;br /&gt;
 *  Save draft mỗi 30s, restore khi mở lại edit&lt;br /&gt;
 *  Backup cho trường hợp stash expire hoặc browser crash&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var SAVE_INTERVAL = 30 * 1000;  // 30 seconds&lt;br /&gt;
    var DRAFT_TTL    = 30 * 24 * 60 * 60 * 1000;  // 30 days&lt;br /&gt;
    var STORAGE_PREFIX = &#039;mw-draft-&#039;;&lt;br /&gt;
&lt;br /&gt;
    function getDraftKey() {&lt;br /&gt;
        return STORAGE_PREFIX + mw.config.get( &#039;wgPageName&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function saveDraft( content ) {&lt;br /&gt;
        try {&lt;br /&gt;
            var data = {&lt;br /&gt;
                content: content,&lt;br /&gt;
                timestamp: Date.now(),&lt;br /&gt;
                page: mw.config.get( &#039;wgPageName&#039; ),&lt;br /&gt;
                user: mw.config.get( &#039;wgUserName&#039; )&lt;br /&gt;
            };&lt;br /&gt;
            localStorage.setItem( getDraftKey(), JSON.stringify( data ) );&lt;br /&gt;
            return true;&lt;br /&gt;
        } catch ( e ) {&lt;br /&gt;
            console.warn( &#039;Failed to save draft:&#039;, e );&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadDraft() {&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = localStorage.getItem( getDraftKey() );&lt;br /&gt;
            if ( !raw ) return null;&lt;br /&gt;
            var data = JSON.parse( raw );&lt;br /&gt;
            if ( Date.now() - data.timestamp &amp;gt; DRAFT_TTL ) {&lt;br /&gt;
                localStorage.removeItem( getDraftKey() );&lt;br /&gt;
                return null;&lt;br /&gt;
            }&lt;br /&gt;
            return data;&lt;br /&gt;
        } catch ( e ) {&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearDraft() {&lt;br /&gt;
        try {&lt;br /&gt;
            localStorage.removeItem( getDraftKey() );&lt;br /&gt;
        } catch ( e ) {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function formatAge( ts ) {&lt;br /&gt;
        var diff = Date.now() - ts;&lt;br /&gt;
        var mins = Math.floor( diff / 60000 );&lt;br /&gt;
        if ( mins &amp;lt; 1 ) return &#039;just now&#039;;&lt;br /&gt;
        if ( mins &amp;lt; 60 ) return mins + &#039; min ago&#039;;&lt;br /&gt;
        var hrs = Math.floor( mins / 60 );&lt;br /&gt;
        if ( hrs &amp;lt; 24 ) return hrs + &#039; hour&#039; + ( hrs &amp;gt; 1 ? &#039;s&#039; : &#039;&#039; ) + &#039; ago&#039;;&lt;br /&gt;
        var days = Math.floor( hrs / 24 );&lt;br /&gt;
        return days + &#039; day&#039; + ( days &amp;gt; 1 ? &#039;s&#039; : &#039;&#039; ) + &#039; ago&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupSourceEditor() {&lt;br /&gt;
        var $textarea = $( &#039;#wpTextbox1&#039; );&lt;br /&gt;
        if ( !$textarea.length ) return;&lt;br /&gt;
&lt;br /&gt;
        // Restore draft on load&lt;br /&gt;
        var draft = loadDraft();&lt;br /&gt;
        if ( draft &amp;amp;&amp;amp; draft.content !== $textarea.val() ) {&lt;br /&gt;
            var age = formatAge( draft.timestamp );&lt;br /&gt;
            var $banner = $(&lt;br /&gt;
                &#039;&amp;lt;div class=&amp;quot;mw-draft-banner&amp;quot; style=&amp;quot;background:#fff3cd; border-left:4px solid #ffc107; padding:10px 14px; margin:8px 0; border-radius:3px;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;strong&amp;gt;📝 Unsaved draft found&amp;lt;/strong&amp;gt; (&#039; + age + &#039;)&amp;lt;br&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;mw-draft-restore&amp;quot; style=&amp;quot;margin-top:6px; margin-right:8px;&amp;quot;&amp;gt;Restore draft&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;mw-draft-discard&amp;quot; style=&amp;quot;margin-top:6px;&amp;quot;&amp;gt;Discard draft&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            );&lt;br /&gt;
            $textarea.before( $banner );&lt;br /&gt;
&lt;br /&gt;
            $banner.find( &#039;.mw-draft-restore&#039; ).on( &#039;click&#039;, function () {&lt;br /&gt;
                $textarea.val( draft.content );&lt;br /&gt;
                $banner.remove();&lt;br /&gt;
            } );&lt;br /&gt;
            $banner.find( &#039;.mw-draft-discard&#039; ).on( &#039;click&#039;, function () {&lt;br /&gt;
                clearDraft();&lt;br /&gt;
                $banner.remove();&lt;br /&gt;
            } );&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Auto-save every 30s&lt;br /&gt;
        var saveTimer = setInterval( function () {&lt;br /&gt;
            saveDraft( $textarea.val() );&lt;br /&gt;
            showSaveIndicator();&lt;br /&gt;
        }, SAVE_INTERVAL );&lt;br /&gt;
&lt;br /&gt;
        // Save on every change (throttled)&lt;br /&gt;
        var changeTimer;&lt;br /&gt;
        $textarea.on( &#039;input&#039;, function () {&lt;br /&gt;
            clearTimeout( changeTimer );&lt;br /&gt;
            changeTimer = setTimeout( function () {&lt;br /&gt;
                saveDraft( $textarea.val() );&lt;br /&gt;
            }, 2000 );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        // Clear draft on successful save&lt;br /&gt;
        $( &#039;#editform&#039; ).on( &#039;submit&#039;, function () {&lt;br /&gt;
            // Wait a bit, then clear if save successful (page redirected)&lt;br /&gt;
            setTimeout( clearDraft, 500 );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        // Save on tab close&lt;br /&gt;
        window.addEventListener( &#039;beforeunload&#039;, function () {&lt;br /&gt;
            saveDraft( $textarea.val() );&lt;br /&gt;
        } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupVisualEditor() {&lt;br /&gt;
        // VE saves to its own state, but we can backup the wikitext periodically&lt;br /&gt;
        mw.hook( &#039;ve.activationComplete&#039; ).add( function () {&lt;br /&gt;
            var saveTimer = setInterval( function () {&lt;br /&gt;
                if ( !mw.libs.ve || !mw.libs.ve.targetLoader ) return;&lt;br /&gt;
                try {&lt;br /&gt;
                    var target = ve.init.target;&lt;br /&gt;
                    if ( target &amp;amp;&amp;amp; target.getSurface ) {&lt;br /&gt;
                        var surface = target.getSurface();&lt;br /&gt;
                        if ( surface ) {&lt;br /&gt;
                            var html = surface.getHtml();&lt;br /&gt;
                            saveDraft( html );&lt;br /&gt;
                            showSaveIndicator();&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                } catch ( e ) {}&lt;br /&gt;
            }, SAVE_INTERVAL );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        mw.hook( &#039;ve.deactivationComplete&#039; ).add( function () {&lt;br /&gt;
            // Don&#039;t clear automatically — user may want to reopen&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        mw.hook( &#039;ve.saveComplete&#039; ).add( function () {&lt;br /&gt;
            clearDraft();&lt;br /&gt;
        } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showSaveIndicator() {&lt;br /&gt;
        var $existing = $( &#039;#mw-draft-indicator&#039; );&lt;br /&gt;
        if ( $existing.length ) {&lt;br /&gt;
            $existing.show().delay( 2000 ).fadeOut( 500 );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        var $indicator = $(&lt;br /&gt;
            &#039;&amp;lt;div id=&amp;quot;mw-draft-indicator&amp;quot; style=&amp;quot;position:fixed; bottom:20px; right:20px; background:#0a0; color:white; padding:6px 12px; border-radius:4px; font-size:0.85em; z-index:9999; box-shadow:0 2px 8px rgba(0,0,0,0.2);&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;💾 Draft saved locally&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
        );&lt;br /&gt;
        $( &#039;body&#039; ).append( $indicator );&lt;br /&gt;
        $indicator.delay( 2000 ).fadeOut( 500, function () { $( this ).remove(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( function () {&lt;br /&gt;
        var action = mw.config.get( &#039;wgAction&#039; );&lt;br /&gt;
        if ( action === &#039;edit&#039; || action === &#039;submit&#039; ) {&lt;br /&gt;
            setupSourceEditor();&lt;br /&gt;
        }&lt;br /&gt;
        if ( mw.config.get( &#039;wgVisualEditor&#039; ) ) {&lt;br /&gt;
            setupVisualEditor();&lt;br /&gt;
        }&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  MultiBoilerplate auto-load on dropdown change&lt;br /&gt;
 *  Use URL navigation instead of form submission (more reliable)&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    $( function () {&lt;br /&gt;
        // Only run on edit action&lt;br /&gt;
        if ( mw.config.get( &#039;wgAction&#039; ) !== &#039;edit&#039; ) return;&lt;br /&gt;
&lt;br /&gt;
        // Find MultiBoilerplate dropdown — try multiple selectors&lt;br /&gt;
        var $select = $( &#039;select[name=&amp;quot;boilerplate&amp;quot;], #multiboilerplate-select, #boilerplate-select&#039; );&lt;br /&gt;
&lt;br /&gt;
        if ( !$select.length ) {&lt;br /&gt;
            console.log( &#039;[MB-AutoLoad] No boilerplate dropdown found on this page&#039; );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        console.log( &#039;[MB-AutoLoad] Found dropdown:&#039;, $select[ 0 ] );&lt;br /&gt;
&lt;br /&gt;
        // Find Load button to hide it&lt;br /&gt;
        var $form = $select.closest( &#039;form&#039; );&lt;br /&gt;
        var $submitBtn = $form.find( &#039;input[type=&amp;quot;submit&amp;quot;], button[type=&amp;quot;submit&amp;quot;]&#039; ).filter( function () {&lt;br /&gt;
            var text = ( $( this ).val() || $( this ).text() || &#039;&#039; ).toLowerCase();&lt;br /&gt;
            return text.indexOf( &#039;load&#039; ) !== -1 || text.indexOf( &#039;boilerplate&#039; ) !== -1;&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        $submitBtn.hide();&lt;br /&gt;
&lt;br /&gt;
        // Add loading indicator&lt;br /&gt;
        var $indicator = $(&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;mb-loading-indicator&amp;quot; style=&amp;quot;margin-left:8px; color:#0054A6; font-style:italic; display:none;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;⏳ Loading template...&#039; +&lt;br /&gt;
            &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
        );&lt;br /&gt;
        $select.after( $indicator );&lt;br /&gt;
&lt;br /&gt;
        // Auto-load on dropdown change&lt;br /&gt;
        $select.on( &#039;change&#039;, function () {&lt;br /&gt;
            var value = $( this ).val();&lt;br /&gt;
&lt;br /&gt;
            console.log( &#039;[MB-AutoLoad] Selected:&#039;, value );&lt;br /&gt;
&lt;br /&gt;
            if ( !value || value === &#039;&#039; || value === &#039;-&#039; ) {&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Show loading&lt;br /&gt;
            $indicator.show();&lt;br /&gt;
            $select.prop( &#039;disabled&#039;, true );&lt;br /&gt;
&lt;br /&gt;
            // Build URL with boilerplate parameter&lt;br /&gt;
            var url = new URL( window.location.href );&lt;br /&gt;
            url.searchParams.set( &#039;boilerplate&#039;, value );&lt;br /&gt;
&lt;br /&gt;
            console.log( &#039;[MB-AutoLoad] Navigating to:&#039;, url.toString() );&lt;br /&gt;
&lt;br /&gt;
            // Small delay for visual feedback&lt;br /&gt;
            setTimeout( function () {&lt;br /&gt;
                window.location.href = url.toString();&lt;br /&gt;
            }, 300 );&lt;br /&gt;
        } );&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
/* Auto-convert spaces to dashes in software-card filename hints */&lt;br /&gt;
$(function() {&lt;br /&gt;
    $(&#039;.software-card-no-icon-text code&#039;).each(function() {&lt;br /&gt;
        var text = $(this).text();&lt;br /&gt;
        // Replace spaces with dashes&lt;br /&gt;
        $(this).text(text.replace(/ /g, &#039;-&#039;));&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Filename hint formatter&lt;br /&gt;
 *  Convert spaces → dashes, strip special chars&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function cleanFilename( text ) {&lt;br /&gt;
        return text&lt;br /&gt;
            .replace( /&amp;amp;amp;/g, &#039;&amp;amp;&#039; )      // decode HTML entity first&lt;br /&gt;
            .replace( /&amp;amp;#38;/g, &#039;&amp;amp;&#039; )&lt;br /&gt;
            .replace( / &amp;amp; /g, &#039;-&#039; )         // &amp;quot; &amp;amp; &amp;quot; → &amp;quot;-&amp;quot;&lt;br /&gt;
            .replace( /&amp;amp;/g, &#039;&#039; )            // remove remaining &amp;amp;&lt;br /&gt;
            .replace( / /g, &#039;-&#039; )           // space → dash&lt;br /&gt;
            .replace( /-+/g, &#039;-&#039; );         // multiple dashes → single&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function formatFilenameHints() {&lt;br /&gt;
        var selectors = [&lt;br /&gt;
            &#039;.cert-card-missing code&#039;,&lt;br /&gt;
            &#039;.software-card-no-icon-text code&#039;,&lt;br /&gt;
            &#039;.accessory-card-no-icon-text code&#039;,&lt;br /&gt;
            &#039;.product-no-image-text code&#039;,&lt;br /&gt;
            &#039;.faq-card-no-icon-text code&#039;&lt;br /&gt;
        ].join( &#039;, &#039; );&lt;br /&gt;
&lt;br /&gt;
        $( selectors ).each( function () {&lt;br /&gt;
            var $code = $( this );&lt;br /&gt;
            if ( $code.data( &#039;filename-formatted&#039; ) ) return;&lt;br /&gt;
&lt;br /&gt;
            var text = $code.text();&lt;br /&gt;
            var formatted = cleanFilename( text );&lt;br /&gt;
&lt;br /&gt;
            if ( formatted !== text ) {&lt;br /&gt;
                $code.text( formatted );&lt;br /&gt;
            }&lt;br /&gt;
            $code.data( &#039;filename-formatted&#039;, &#039;1&#039; );&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( formatFilenameHints );&lt;br /&gt;
&lt;br /&gt;
    if ( window.MutationObserver ) {&lt;br /&gt;
        var observer = new MutationObserver( function () {&lt;br /&gt;
            formatFilenameHints();&lt;br /&gt;
        });&lt;br /&gt;
        observer.observe( document.body, { childList: true, subtree: true } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
} )( jQuery );&lt;br /&gt;
&lt;br /&gt;
/* Real-time filter for dynamically loaded sidebar items */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var BLOCKED_PATTERNS = [&lt;br /&gt;
        &#039;Product_Change_Notifications&#039;,&lt;br /&gt;
        &#039;Product Change Notifications&#039;,&lt;br /&gt;
        &#039;Firmware_Changelog&#039;,&lt;br /&gt;
        &#039;Firmware Changelog&#039;,&lt;br /&gt;
        &#039;Promotional_Material&#039;,&lt;br /&gt;
        &#039;Promotional Material&#039;,&lt;br /&gt;
        &#039;Certifications&#039;,&lt;br /&gt;
        &#039;Certification_%26_Approvals&#039;,&lt;br /&gt;
        &#039;Certification &amp;amp; Approvals&#039;&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function hideBlocked( context ) {&lt;br /&gt;
        var $context = context ? $( context ) : $( document );&lt;br /&gt;
        $context.find( &#039;#p-categorytree-portlet a, #mw-panel a&#039; ).each( function () {&lt;br /&gt;
            var $link = $( this );&lt;br /&gt;
            var href = $link.attr( &#039;href&#039; ) || &#039;&#039;;&lt;br /&gt;
            var text = $link.text();&lt;br /&gt;
&lt;br /&gt;
            for ( var i = 0; i &amp;lt; BLOCKED_PATTERNS.length; i++ ) {&lt;br /&gt;
                var pattern = BLOCKED_PATTERNS[ i ];&lt;br /&gt;
                if ( href.indexOf( pattern ) !== -1 || text.indexOf( pattern.replace( /_/g, &#039; &#039; ) ) !== -1 ) {&lt;br /&gt;
                    $link.closest( &#039;li, .CategoryTreeItem&#039; ).addClass( &#039;sidebar-hidden&#039; );&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Run immediately&lt;br /&gt;
    hideBlocked();&lt;br /&gt;
&lt;br /&gt;
    // Observe sidebar for dynamic content&lt;br /&gt;
    if ( window.MutationObserver ) {&lt;br /&gt;
        var sidebar = document.querySelector( &#039;#p-categorytree-portlet, #mw-panel&#039; );&lt;br /&gt;
        if ( sidebar ) {&lt;br /&gt;
            new MutationObserver( function ( mutations ) {&lt;br /&gt;
                mutations.forEach( function ( m ) {&lt;br /&gt;
                    if ( m.addedNodes.length &amp;gt; 0 ) {&lt;br /&gt;
                        hideBlocked( m.target );&lt;br /&gt;
                    }&lt;br /&gt;
                });&lt;br /&gt;
            }).observe( sidebar, {&lt;br /&gt;
                childList: true,&lt;br /&gt;
                subtree: true&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
})( jQuery );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Auto-play video on scroll into view (IntersectionObserver)&lt;br /&gt;
 *  Play when visible, pause when scrolled out, loop infinitely&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function setupScrollVideos() {&lt;br /&gt;
        if ( !( &#039;IntersectionObserver&#039; in window ) ) return;&lt;br /&gt;
&lt;br /&gt;
        var videos = document.querySelectorAll( &#039;video.scroll-play, .scroll-play video&#039; );&lt;br /&gt;
        if ( !videos.length ) return;&lt;br /&gt;
&lt;br /&gt;
        var observer = new IntersectionObserver( function ( entries ) {&lt;br /&gt;
            entries.forEach( function ( entry ) {&lt;br /&gt;
                var video = entry.target;&lt;br /&gt;
                if ( entry.isIntersecting ) {&lt;br /&gt;
                    video.play().catch( function () {&lt;br /&gt;
                        // Autoplay blocked by browser, mute and try again&lt;br /&gt;
                        video.muted = true;&lt;br /&gt;
                        video.play();&lt;br /&gt;
                    });&lt;br /&gt;
                } else {&lt;br /&gt;
                    video.pause();&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }, {&lt;br /&gt;
            threshold: 0.5  // play when 50% visible&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        videos.forEach( function ( video ) {&lt;br /&gt;
            video.muted = true;        // required for autoplay in most browsers&lt;br /&gt;
            video.loop = true;          // infinite loop&lt;br /&gt;
            video.playsInline = true;   // iOS Safari support&lt;br /&gt;
            observer.observe( video );&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( document.readyState !== &#039;loading&#039; ) {&lt;br /&gt;
        setupScrollVideos();&lt;br /&gt;
    } else {&lt;br /&gt;
        document.addEventListener( &#039;DOMContentLoaded&#039;, setupScrollVideos );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
} )();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* =========================&lt;br /&gt;
   YOUTUBE EMBED via JS&lt;br /&gt;
   Template chỉ output data, JS generate full embed&lt;br /&gt;
   ========================= */&lt;br /&gt;
(function () {&lt;br /&gt;
    function buildEmbed(container) {&lt;br /&gt;
        if (container.dataset.initialized === &#039;1&#039;) return;&lt;br /&gt;
        container.dataset.initialized = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
        var videoId = container.dataset.videoId;&lt;br /&gt;
        var width = container.dataset.width || &#039;640&#039;;&lt;br /&gt;
        var align = container.dataset.align || &#039;center&#039;;&lt;br /&gt;
        var caption = container.dataset.caption || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (!videoId) {&lt;br /&gt;
            container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#d33&amp;quot;&amp;gt;Error: missing video ID&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Build container styles&lt;br /&gt;
        container.style.textAlign = align;&lt;br /&gt;
        container.style.margin = &#039;12px 0&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Thumbnail wrapper&lt;br /&gt;
        var thumbWrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        thumbWrap.className = &#039;soji-yt-thumb-wrap&#039;;&lt;br /&gt;
        thumbWrap.style.cssText = &lt;br /&gt;
            &#039;position: relative;&#039; +&lt;br /&gt;
            &#039;display: inline-block;&#039; +&lt;br /&gt;
            &#039;width: &#039; + width + &#039;px;&#039; +&lt;br /&gt;
            &#039;max-width: 100%;&#039; +&lt;br /&gt;
            &#039;aspect-ratio: 16/9;&#039; +&lt;br /&gt;
            &#039;cursor: pointer;&#039; +&lt;br /&gt;
            &#039;overflow: hidden;&#039; +&lt;br /&gt;
            &#039;border-radius: 8px;&#039; +&lt;br /&gt;
            &#039;background: #000;&#039; +&lt;br /&gt;
            &#039;box-shadow: 0 4px 12px rgba(0,0,0,0.15);&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Thumbnail image&lt;br /&gt;
        var img = document.createElement(&#039;img&#039;);&lt;br /&gt;
        img.src = &#039;https://i.ytimg.com/vi/&#039; + videoId + &#039;/hqdefault.jpg&#039;;&lt;br /&gt;
        img.alt = &#039;Video thumbnail&#039;;&lt;br /&gt;
        img.loading = &#039;lazy&#039;;&lt;br /&gt;
        img.style.cssText = &lt;br /&gt;
            &#039;width: 100%;&#039; +&lt;br /&gt;
            &#039;height: 100%;&#039; +&lt;br /&gt;
            &#039;object-fit: cover;&#039; +&lt;br /&gt;
            &#039;display: block;&#039; +&lt;br /&gt;
            &#039;transition: transform 0.3s, filter 0.2s;&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Try maxresdefault for better quality&lt;br /&gt;
        var hiresTest = new Image();&lt;br /&gt;
        hiresTest.onload = function () {&lt;br /&gt;
            if (hiresTest.naturalWidth &amp;gt; 480) {&lt;br /&gt;
                img.src = &#039;https://i.ytimg.com/vi/&#039; + videoId + &#039;/maxresdefault.jpg&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        };&lt;br /&gt;
        hiresTest.src = &#039;https://i.ytimg.com/vi/&#039; + videoId + &#039;/maxresdefault.jpg&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Play button overlay&lt;br /&gt;
        var overlay = document.createElement(&#039;div&#039;);&lt;br /&gt;
        overlay.className = &#039;soji-yt-play-overlay&#039;;&lt;br /&gt;
        overlay.style.cssText = &lt;br /&gt;
            &#039;position: absolute;&#039; +&lt;br /&gt;
            &#039;top: 50%;&#039; +&lt;br /&gt;
            &#039;left: 50%;&#039; +&lt;br /&gt;
            &#039;transform: translate(-50%, -50%);&#039; +&lt;br /&gt;
            &#039;width: 72px;&#039; +&lt;br /&gt;
            &#039;height: 50px;&#039; +&lt;br /&gt;
            &#039;background: rgba(0, 0, 0, 0.7);&#039; +&lt;br /&gt;
            &#039;border-radius: 14px;&#039; +&lt;br /&gt;
            &#039;display: flex;&#039; +&lt;br /&gt;
            &#039;align-items: center;&#039; +&lt;br /&gt;
            &#039;justify-content: center;&#039; +&lt;br /&gt;
            &#039;transition: background 0.2s, transform 0.2s;&#039; +&lt;br /&gt;
            &#039;pointer-events: none;&#039;;&lt;br /&gt;
&lt;br /&gt;
        overlay.innerHTML = &lt;br /&gt;
            &#039;&amp;lt;svg width=&amp;quot;36&amp;quot; height=&amp;quot;26&amp;quot; viewBox=&amp;quot;0 0 36 26&amp;quot; fill=&amp;quot;white&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;polygon points=&amp;quot;14,7 14,19 25,13&amp;quot;/&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/svg&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
        thumbWrap.appendChild(img);&lt;br /&gt;
        thumbWrap.appendChild(overlay);&lt;br /&gt;
&lt;br /&gt;
        // Hover effect&lt;br /&gt;
        thumbWrap.addEventListener(&#039;mouseenter&#039;, function () {&lt;br /&gt;
            overlay.style.background = &#039;#f00&#039;;&lt;br /&gt;
            overlay.style.transform = &#039;translate(-50%, -50%) scale(1.1)&#039;;&lt;br /&gt;
            img.style.filter = &#039;brightness(0.85)&#039;;&lt;br /&gt;
        });&lt;br /&gt;
        thumbWrap.addEventListener(&#039;mouseleave&#039;, function () {&lt;br /&gt;
            overlay.style.background = &#039;rgba(0, 0, 0, 0.7)&#039;;&lt;br /&gt;
            overlay.style.transform = &#039;translate(-50%, -50%) scale(1)&#039;;&lt;br /&gt;
            img.style.filter = &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // Click → load iframe&lt;br /&gt;
        thumbWrap.addEventListener(&#039;click&#039;, function () {&lt;br /&gt;
            var iframe = document.createElement(&#039;iframe&#039;);&lt;br /&gt;
            iframe.src = &#039;https://www.youtube-nocookie.com/embed/&#039; + videoId + &lt;br /&gt;
                         &#039;?autoplay=1&amp;amp;rel=0&amp;amp;modestbranding=1&amp;amp;playsinline=1&#039;;&lt;br /&gt;
            iframe.style.cssText = &lt;br /&gt;
                &#039;width: 100%;&#039; +&lt;br /&gt;
                &#039;height: 100%;&#039; +&lt;br /&gt;
                &#039;border: 0;&#039; +&lt;br /&gt;
                &#039;position: absolute;&#039; +&lt;br /&gt;
                &#039;inset: 0;&#039;;&lt;br /&gt;
            iframe.setAttribute(&#039;frameborder&#039;, &#039;0&#039;);&lt;br /&gt;
            iframe.setAttribute(&#039;allow&#039;, &#039;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&#039;);&lt;br /&gt;
            iframe.setAttribute(&#039;allowfullscreen&#039;, &#039;true&#039;);&lt;br /&gt;
&lt;br /&gt;
            thumbWrap.innerHTML = &#039;&#039;;&lt;br /&gt;
            thumbWrap.appendChild(iframe);&lt;br /&gt;
            thumbWrap.style.cursor = &#039;default&#039;;&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        container.appendChild(thumbWrap);&lt;br /&gt;
&lt;br /&gt;
        // Caption&lt;br /&gt;
        if (caption) {&lt;br /&gt;
            var captionDiv = document.createElement(&#039;div&#039;);&lt;br /&gt;
            captionDiv.style.cssText = &lt;br /&gt;
                &#039;margin-top: 8px;&#039; +&lt;br /&gt;
                &#039;font-style: italic;&#039; +&lt;br /&gt;
                &#039;color: #666;&#039; +&lt;br /&gt;
                &#039;font-size: 0.9em;&#039;;&lt;br /&gt;
            captionDiv.textContent = caption;&lt;br /&gt;
            container.appendChild(captionDiv);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initYouTubeEmbeds() {&lt;br /&gt;
        document.querySelectorAll(&#039;.soji-yt-embed&#039;).forEach(buildEmbed);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, initYouTubeEmbeds);&lt;br /&gt;
    } else {&lt;br /&gt;
        initYouTubeEmbeds();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Floating Buttons: move to body level&lt;br /&gt;
 *  Escape mw-body-content stacking context&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
(function () {&lt;br /&gt;
    function moveFloatingButtonsToBody() {&lt;br /&gt;
        var fb = document.getElementById(&#039;floating-buttons&#039;);&lt;br /&gt;
        if (!fb) return;&lt;br /&gt;
        &lt;br /&gt;
        // Already at body level → skip&lt;br /&gt;
        if (fb.parentElement === document.body) return;&lt;br /&gt;
        &lt;br /&gt;
        document.body.appendChild(fb);&lt;br /&gt;
        &lt;br /&gt;
        // Force styles&lt;br /&gt;
        fb.style.position = &#039;fixed&#039;;&lt;br /&gt;
        fb.style.zIndex = &#039;99999&#039;;&lt;br /&gt;
        fb.style.bottom = &#039;24px&#039;;&lt;br /&gt;
        fb.style.right = &#039;24px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, moveFloatingButtonsToBody);&lt;br /&gt;
    } else {&lt;br /&gt;
        moveFloatingButtonsToBody();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Re-check sau 1s để chắc chắn (nếu floating-buttons được tạo bằng JS khác sau)&lt;br /&gt;
    setTimeout(moveFloatingButtonsToBody, 1000);&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
/* =========================&lt;br /&gt;
   SCROLL VIDEO - local video with autoplay on scroll&lt;br /&gt;
   ========================= */&lt;br /&gt;
(function () {&lt;br /&gt;
    function buildScrollVideo(container) {&lt;br /&gt;
        if (container.dataset.initialized === &#039;1&#039;) return;&lt;br /&gt;
        container.dataset.initialized = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
        var src = container.dataset.src;&lt;br /&gt;
        var width = container.dataset.width || &#039;640&#039;;&lt;br /&gt;
        var caption = container.dataset.caption || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (!src) {&lt;br /&gt;
            container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#d33&amp;quot;&amp;gt;Error: missing video src&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        container.style.textAlign = &#039;center&#039;;&lt;br /&gt;
        container.style.margin = &#039;12px 0&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Create video element&lt;br /&gt;
        var video = document.createElement(&#039;video&#039;);&lt;br /&gt;
        video.className = &#039;scroll-play&#039;;&lt;br /&gt;
        video.width = width;&lt;br /&gt;
        video.muted = true;&lt;br /&gt;
        video.loop = true;&lt;br /&gt;
        video.playsInline = true;&lt;br /&gt;
        video.preload = &#039;metadata&#039;;&lt;br /&gt;
        video.style.cssText = &#039;max-width:100%; border-radius:8px; box-shadow:0 2px 8px rgba(0,0,0,0.1);&#039;;&lt;br /&gt;
&lt;br /&gt;
        var source = document.createElement(&#039;source&#039;);&lt;br /&gt;
        source.src = src;&lt;br /&gt;
        source.type = &#039;video/mp4&#039;;&lt;br /&gt;
        video.appendChild(source);&lt;br /&gt;
        video.appendChild(document.createTextNode(&#039;Your browser does not support HTML5 video.&#039;));&lt;br /&gt;
&lt;br /&gt;
        container.appendChild(video);&lt;br /&gt;
&lt;br /&gt;
        // Caption&lt;br /&gt;
        if (caption) {&lt;br /&gt;
            var cap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            cap.style.cssText = &#039;margin-top:8px; font-style:italic; color:#666; font-size:0.9em;&#039;;&lt;br /&gt;
            cap.textContent = caption;&lt;br /&gt;
            container.appendChild(cap);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Setup IntersectionObserver for scroll autoplay&lt;br /&gt;
        if (&#039;IntersectionObserver&#039; in window) {&lt;br /&gt;
            var observer = new IntersectionObserver(function (entries) {&lt;br /&gt;
                entries.forEach(function (entry) {&lt;br /&gt;
                    if (entry.isIntersecting) {&lt;br /&gt;
                        video.play().catch(function () {});&lt;br /&gt;
                    } else {&lt;br /&gt;
                        video.pause();&lt;br /&gt;
                    }&lt;br /&gt;
                });&lt;br /&gt;
            }, { threshold: 0.5 });&lt;br /&gt;
            observer.observe(video);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initScrollVideos() {&lt;br /&gt;
        document.querySelectorAll(&#039;.soji-scroll-video&#039;).forEach(buildScrollVideo);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, initScrollVideos);&lt;br /&gt;
    } else {&lt;br /&gt;
        initScrollVideos();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
/* ========================================================================&lt;br /&gt;
 * Release Marker Checkbox — Edit form (Source + VisualEditor save dialog)&lt;br /&gt;
 * Tick → append [release] to edit summary → revision shows in history&lt;br /&gt;
 * ======================================================================== */&lt;br /&gt;
( function () {&lt;br /&gt;
	var MARKER = &#039;[release]&#039;;&lt;br /&gt;
	var lastDialogVisible = false;&lt;br /&gt;
&lt;br /&gt;
	function isVisible( el ) {&lt;br /&gt;
		return el &amp;amp;&amp;amp; el.offsetParent !== null;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function isSummaryField( el ) {&lt;br /&gt;
		if ( !el || !isVisible( el ) ) { return false; }&lt;br /&gt;
&lt;br /&gt;
		var id = ( el.id || &#039;&#039; ).toLowerCase();&lt;br /&gt;
		var name = ( el.name || &#039;&#039; ).toLowerCase();&lt;br /&gt;
		if ( id.indexOf( &#039;summary&#039; ) !== -1 || name.indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
&lt;br /&gt;
		var p = el.parentElement;&lt;br /&gt;
		for ( var i = 0; i &amp;lt; 6 &amp;amp;&amp;amp; p; i++ ) {&lt;br /&gt;
			var cls = ( p.className || &#039;&#039; ).toString().toLowerCase();&lt;br /&gt;
			if ( cls.indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
			p = p.parentElement;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var lblId = el.getAttribute( &#039;aria-labelledby&#039; );&lt;br /&gt;
		if ( lblId ) {&lt;br /&gt;
			var lblEl = document.getElementById( lblId );&lt;br /&gt;
			if ( lblEl &amp;amp;&amp;amp; lblEl.textContent.toLowerCase().indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var fl = el.closest( &#039;.oo-ui-fieldLayout&#039; );&lt;br /&gt;
		if ( fl ) {&lt;br /&gt;
			var label = fl.querySelector( &#039;.oo-ui-labelElement-label, label&#039; );&lt;br /&gt;
			if ( label &amp;amp;&amp;amp; label.textContent.toLowerCase().indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function findSummaryField() {&lt;br /&gt;
		var all = document.querySelectorAll( &#039;textarea, input[type=&amp;quot;text&amp;quot;]&#039; );&lt;br /&gt;
		for ( var i = 0; i &amp;lt; all.length; i++ ) {&lt;br /&gt;
			if ( isSummaryField( all[ i ] ) ) { return all[ i ]; }&lt;br /&gt;
		}&lt;br /&gt;
		return null;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function getContainer( field ) {&lt;br /&gt;
		return field.closest( &#039;.ve-ui-mwSaveDialog, .oo-ui-dialog-content, .editOptions, form&#039; )&lt;br /&gt;
			|| ( field.parentElement &amp;amp;&amp;amp; field.parentElement.parentElement )&lt;br /&gt;
			|| document;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function cleanupStaleWrappers() {&lt;br /&gt;
		document.querySelectorAll( &#039;.release-checkbox-wrapper&#039; ).forEach( function ( w ) {&lt;br /&gt;
			if ( !isVisible( w ) ) { w.remove(); }&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function resetAllCheckboxes() {&lt;br /&gt;
		document.querySelectorAll( &#039;.mark-as-release-cb&#039; ).forEach( function ( cb ) {&lt;br /&gt;
			cb.checked = false;&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function injectCheckbox( field ) {&lt;br /&gt;
		if ( !field ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var anchor =&lt;br /&gt;
			field.closest( &#039;.ve-ui-mwSaveDialog-summary&#039; ) ||&lt;br /&gt;
			field.closest( &#039;.oo-ui-fieldLayout&#039; ) ||&lt;br /&gt;
			field.closest( &#039;.oo-ui-textInputWidget&#039; ) ||&lt;br /&gt;
			field.parentElement;&lt;br /&gt;
		if ( !anchor || !anchor.parentNode ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var container = getContainer( field );&lt;br /&gt;
		var existing = container.querySelector( &#039;.release-checkbox-wrapper&#039; );&lt;br /&gt;
		if ( existing &amp;amp;&amp;amp; isVisible( existing ) ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var wrapper = document.createElement( &#039;div&#039; );&lt;br /&gt;
		wrapper.className = &#039;release-checkbox-wrapper&#039;;&lt;br /&gt;
		wrapper.style.cssText = &#039;margin:8px 0;padding:8px 12px;background:#f0f8ff;border-left:3px solid #1f4e79;border-radius:3px;font-size:13px;display:block;box-sizing:border-box;&#039;;&lt;br /&gt;
		wrapper.innerHTML =&lt;br /&gt;
			&#039;&amp;lt;label style=&amp;quot;cursor:pointer;font-weight:500;display:block;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
			&#039;&amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;mark-as-release-cb&amp;quot; style=&amp;quot;margin-right:6px;vertical-align:middle;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
			&#039;Mark as published revision&#039; +&lt;br /&gt;
			&#039;&amp;lt;/label&amp;gt;&#039; +&lt;br /&gt;
			&#039;&amp;lt;div style=&amp;quot;font-size:11px;color:#666;margin-top:3px;margin-left:22px;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
			&#039;Tick to add this edit to the Revision History table.&#039; +&lt;br /&gt;
			&#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
		anchor.parentNode.insertBefore( wrapper, anchor );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function applyMarkerIfChecked() {&lt;br /&gt;
		var field = findSummaryField();&lt;br /&gt;
		if ( !field ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var container = getContainer( field );&lt;br /&gt;
		var cb = container.querySelector( &#039;.mark-as-release-cb&#039; );&lt;br /&gt;
		if ( !cb || !isVisible( cb ) || !cb.checked ) { return; }&lt;br /&gt;
&lt;br /&gt;
		if ( field.value.indexOf( MARKER ) === -1 ) {&lt;br /&gt;
			field.value = ( field.value.trim() ? field.value.trim() + &#039; &#039; : &#039;&#039; ) + MARKER;&lt;br /&gt;
			field.dispatchEvent( new Event( &#039;input&#039;, { bubbles: true } ) );&lt;br /&gt;
			field.dispatchEvent( new Event( &#039;change&#039;, { bubbles: true } ) );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// Reset checkbox after applying — prevents stale state on next save&lt;br /&gt;
		cb.checked = false;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	document.addEventListener( &#039;click&#039;, function ( e ) {&lt;br /&gt;
		var btn = e.target.closest( &#039;button, a[role=&amp;quot;button&amp;quot;], .oo-ui-buttonElement-button&#039; );&lt;br /&gt;
		if ( !btn ) { return; }&lt;br /&gt;
		var text = ( btn.textContent || &#039;&#039; ).trim().toLowerCase();&lt;br /&gt;
		if (&lt;br /&gt;
			text === &#039;save changes&#039; || text === &#039;save page&#039; ||&lt;br /&gt;
			text === &#039;publish changes&#039; || text === &#039;publish page&#039; ||&lt;br /&gt;
			text === &#039;lưu thay đổi&#039; || text === &#039;lưu trang&#039; ||&lt;br /&gt;
			btn.id === &#039;wpSave&#039;&lt;br /&gt;
		) {&lt;br /&gt;
			applyMarkerIfChecked();&lt;br /&gt;
		}&lt;br /&gt;
	}, true );&lt;br /&gt;
&lt;br /&gt;
	document.addEventListener( &#039;submit&#039;, function ( e ) {&lt;br /&gt;
		var form = e.target;&lt;br /&gt;
		if ( form &amp;amp;&amp;amp; ( form.id === &#039;editform&#039; || form.querySelector( &#039;#wpSummary&#039; ) ) ) {&lt;br /&gt;
			applyMarkerIfChecked();&lt;br /&gt;
		}&lt;br /&gt;
	}, true );&lt;br /&gt;
&lt;br /&gt;
	function scan() {&lt;br /&gt;
		cleanupStaleWrappers();&lt;br /&gt;
		var field = findSummaryField();&lt;br /&gt;
		var dialogVisible = !!field;&lt;br /&gt;
&lt;br /&gt;
		// Reset checkboxes when dialog transitions from closed -&amp;gt; open&lt;br /&gt;
		if ( dialogVisible &amp;amp;&amp;amp; !lastDialogVisible ) {&lt;br /&gt;
			resetAllCheckboxes();&lt;br /&gt;
		}&lt;br /&gt;
		lastDialogVisible = dialogVisible;&lt;br /&gt;
&lt;br /&gt;
		if ( field ) { injectCheckbox( field ); }&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if ( document.readyState === &#039;loading&#039; ) {&lt;br /&gt;
		document.addEventListener( &#039;DOMContentLoaded&#039;, scan );&lt;br /&gt;
	} else {&lt;br /&gt;
		scan();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	var observer = new MutationObserver( scan );&lt;br /&gt;
	observer.observe( document.body, {&lt;br /&gt;
		childList: true,&lt;br /&gt;
		subtree: true,&lt;br /&gt;
		attributes: true,&lt;br /&gt;
		attributeFilter: [ &#039;style&#039;, &#039;class&#039; ]&lt;br /&gt;
	} );&lt;br /&gt;
&lt;br /&gt;
	setInterval( scan, 500 );&lt;br /&gt;
}() );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ========================================================================&lt;br /&gt;
 * Revision History Renderer — show only revisions tagged with [release]&lt;br /&gt;
 * Auto-version v1.0, v1.1, v1.2 ... in chronological order&lt;br /&gt;
 * ======================================================================== */&lt;br /&gt;
( function () {&lt;br /&gt;
	function formatDate( ts ) {&lt;br /&gt;
		var d = new Date( ts );&lt;br /&gt;
		var dd = String( d.getDate() ).padStart( 2, &#039;0&#039; );&lt;br /&gt;
		var mm = String( d.getMonth() + 1 ).padStart( 2, &#039;0&#039; );&lt;br /&gt;
		var yyyy = d.getFullYear();&lt;br /&gt;
		return dd + &#039;/&#039; + mm + &#039;/&#039; + yyyy;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function hasReleaseMarker( comment ) {&lt;br /&gt;
		if ( !comment ) { return false; }&lt;br /&gt;
		return /(^|\s)\[release\](\s|$)/.test( comment );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function cleanComment( comment ) {&lt;br /&gt;
		if ( !comment ) { return &#039;Initial Release&#039;; }&lt;br /&gt;
		var cleaned = comment&lt;br /&gt;
			.replace( /\[release\]/g, &#039;&#039; )&lt;br /&gt;
			.replace( /\/\*[^*]*\*\//g, &#039;&#039; )&lt;br /&gt;
			.trim();&lt;br /&gt;
		return cleaned || &#039;Updated&#039;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function renderRevisionHistory( container ) {&lt;br /&gt;
		var api = new mw.Api();&lt;br /&gt;
		api.get( {&lt;br /&gt;
			action: &#039;query&#039;,&lt;br /&gt;
			prop: &#039;revisions&#039;,&lt;br /&gt;
			titles: mw.config.get( &#039;wgPageName&#039; ),&lt;br /&gt;
			rvprop: &#039;timestamp|user|comment|ids&#039;,&lt;br /&gt;
			rvlimit: 500&lt;br /&gt;
		} ).done( function ( data ) {&lt;br /&gt;
			var pages = data.query &amp;amp;&amp;amp; data.query.pages;&lt;br /&gt;
			if ( !pages ) { return; }&lt;br /&gt;
			var pageId = Object.keys( pages )[ 0 ];&lt;br /&gt;
			var revisions = pages[ pageId ].revisions || [];&lt;br /&gt;
&lt;br /&gt;
			var releases = revisions.filter( function ( r ) {&lt;br /&gt;
				return hasReleaseMarker( r.comment );&lt;br /&gt;
			} );&lt;br /&gt;
&lt;br /&gt;
			if ( !releases.length ) {&lt;br /&gt;
				container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#888;&amp;quot;&amp;gt;No published revisions yet. Tick &amp;quot;Mark as published revision&amp;quot; when saving an edit to add an entry here.&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			releases.sort( function ( a, b ) {&lt;br /&gt;
				return new Date( a.timestamp ) - new Date( b.timestamp );&lt;br /&gt;
			} );&lt;br /&gt;
			releases.forEach( function ( r, i ) {&lt;br /&gt;
				r.version = &#039;v1.&#039; + i;&lt;br /&gt;
			} );&lt;br /&gt;
			releases.reverse();&lt;br /&gt;
&lt;br /&gt;
			var html = &#039;&amp;lt;table class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
				&#039;&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Date&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Version&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Author&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&#039; +&lt;br /&gt;
				&#039;&amp;lt;tbody&amp;gt;&#039;;&lt;br /&gt;
			releases.forEach( function ( r ) {&lt;br /&gt;
				html +=&lt;br /&gt;
					&#039;&amp;lt;tr&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&#039; + formatDate( r.timestamp ) + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&amp;lt;strong&amp;gt;&#039; + r.version + &#039;&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&#039; + mw.html.escape( r.user ) + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&#039; + mw.html.escape( cleanComment( r.comment ) ) + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;/tr&amp;gt;&#039;;&lt;br /&gt;
			} );&lt;br /&gt;
			html += &#039;&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;&#039;;&lt;br /&gt;
			container.innerHTML = html;&lt;br /&gt;
		} ).fail( function () {&lt;br /&gt;
			container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#c00;&amp;quot;&amp;gt;Failed to load revision history.&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	mw.hook( &#039;wikipage.content&#039; ).add( function ( $content ) {&lt;br /&gt;
		$content.find( &#039;.revision-history-auto&#039; ).each( function () {&lt;br /&gt;
			renderRevisionHistory( this );&lt;br /&gt;
		} );&lt;br /&gt;
	} );&lt;br /&gt;
}() );&lt;br /&gt;
&lt;br /&gt;
/* ========================================================================&lt;br /&gt;
 * PDF TOC Generator with JS-measured page numbers&lt;br /&gt;
 * No forced page breaks - measures actual heading positions&lt;br /&gt;
 * ======================================================================== */&lt;br /&gt;
/* ===== PDF mode (pdf-service entry point) ===== */&lt;br /&gt;
( function () {&lt;br /&gt;
	&#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
	var params = new URLSearchParams( location.search );&lt;br /&gt;
	var pdfMode = params.get( &#039;pdfmode&#039; ) === &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
	if ( pdfMode ) {&lt;br /&gt;
		document.documentElement.classList.add( &#039;pdfmode&#039; );&lt;br /&gt;
		document.body &amp;amp;&amp;amp; document.body.classList.add( &#039;pdfmode&#039; );&lt;br /&gt;
		document.addEventListener( &#039;DOMContentLoaded&#039;, function () {&lt;br /&gt;
			document.body.classList.add( &#039;pdfmode&#039; );&lt;br /&gt;
		} );&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function addDownloadLink() {&lt;br /&gt;
		var title = ( mw.config &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
		var ns = mw.config &amp;amp;&amp;amp; mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
		if ( !title || ns !== 0 ) { return; }&lt;br /&gt;
&lt;br /&gt;
		// Show only on Datasheet / User Guide / Protocol pages, or Software &amp;amp; Applications category&lt;br /&gt;
		var titleMatches = /(Datasheet|User[_ ]Guide|Protocol)/i.test( title );&lt;br /&gt;
		var categories = ( mw.config &amp;amp;&amp;amp; mw.config.get( &#039;wgCategories&#039; ) ) || [];&lt;br /&gt;
		var inSoftwareCat = categories.some( function ( c ) {&lt;br /&gt;
			return /Software\s*&amp;amp;?\s*Applications/i.test( c );&lt;br /&gt;
		} );&lt;br /&gt;
		if ( !titleMatches &amp;amp;&amp;amp; !inSoftwareCat ) { return; }&lt;br /&gt;
&lt;br /&gt;
		if ( document.querySelector( &#039;.pdf-download-btn&#039; ) ) { return; }&lt;br /&gt;
		var anchor = document.querySelector( &#039;#firstHeading&#039; );&lt;br /&gt;
		if ( !anchor ) { return; }&lt;br /&gt;
		var btn = document.createElement( &#039;a&#039; );&lt;br /&gt;
		btn.className = &#039;pdf-download-btn&#039;;&lt;br /&gt;
		btn.href = &#039;/api/pdf?page=&#039; + encodeURIComponent( title );&lt;br /&gt;
		btn.target = &#039;_blank&#039;;&lt;br /&gt;
		btn.rel = &#039;noopener&#039;;&lt;br /&gt;
		btn.innerHTML = &#039;&amp;lt;span class=&amp;quot;pdf-download-icon&amp;quot;&amp;gt;\u2B07&amp;lt;/span&amp;gt; Download PDF&#039;;&lt;br /&gt;
		anchor.parentNode.insertBefore( btn, anchor.nextSibling );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if ( !pdfMode &amp;amp;&amp;amp; window.mw &amp;amp;&amp;amp; mw.hook ) {&lt;br /&gt;
		mw.hook( &#039;wikipage.content&#039; ).add( addDownloadLink );&lt;br /&gt;
	}&lt;br /&gt;
}() );&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Thứ tự mong muốn (viết thường)&lt;br /&gt;
    var ORDER = [&#039;datasheet&#039;, &#039;user guide&#039;, &#039;protocol&#039;, &#039;video&#039;];&lt;br /&gt;
&lt;br /&gt;
    function rank(text) {&lt;br /&gt;
        text = (text || &#039;&#039;).toLowerCase();&lt;br /&gt;
        for (var i = 0; i &amp;lt; ORDER.length; i++) {&lt;br /&gt;
            if (text.indexOf(ORDER[i]) !== -1) return i;&lt;br /&gt;
        }&lt;br /&gt;
        return ORDER.length; // loại chưa khai báo -&amp;gt; cuối&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Lấy text của label trực tiếp trong section (không lẫn con cháu)&lt;br /&gt;
    function keyText(sec) {&lt;br /&gt;
        for (var i = 0; i &amp;lt; sec.children.length; i++) {&lt;br /&gt;
            if (sec.children[i].classList.contains(&#039;CategoryTreeItem&#039;)) {&lt;br /&gt;
                return sec.children[i].textContent;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return sec.textContent;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function sortContainer(container) {&lt;br /&gt;
        var sections = Array.prototype.filter.call(container.children, function (el) {&lt;br /&gt;
            return el.classList &amp;amp;&amp;amp; el.classList.contains(&#039;CategoryTreeSection&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        if (sections.length &amp;lt; 2) return;&lt;br /&gt;
&lt;br /&gt;
        var sorted = sections.slice().sort(function (a, b) {&lt;br /&gt;
            return rank(keyText(a)) - rank(keyText(b));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // chỉ di chuyển nếu khác (tránh observer lặp vô hạn)&lt;br /&gt;
        var changed = sorted.some(function (el, i) { return el !== sections[i]; });&lt;br /&gt;
        if (changed) {&lt;br /&gt;
            sorted.forEach(function (el) { container.appendChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function run() {&lt;br /&gt;
        document.querySelectorAll(&#039;.CategoryTreeChildren&#039;).forEach(sortContainer);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    run();&lt;br /&gt;
&lt;br /&gt;
    // CategoryTree bung con kiểu lazy -&amp;gt; theo dõi &amp;amp; sắp lại&lt;br /&gt;
    var root = document.querySelector(&#039;#mw-panel&#039;) || document.body;&lt;br /&gt;
    if (root) {&lt;br /&gt;
        new MutationObserver(run).observe(root, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* Sidebar: bấm mục Category thì mở Page cùng tên thay vì trang Category */&lt;br /&gt;
mw.loader.using([&#039;mediawiki.util&#039;]).then(function () {&lt;br /&gt;
  $(&#039;#mw-panel&#039;).on(&#039;click&#039;, &#039;a[href*=&amp;quot;/Category:&amp;quot;]&#039;, function (e) {&lt;br /&gt;
    e.preventDefault();&lt;br /&gt;
    window.location.href = this.href.replace(&#039;/Category:&#039;, &#039;/&#039;);&lt;br /&gt;
  });&lt;br /&gt;
});&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_User_Guide&amp;diff=2082</id>
		<title>LIGO PRO 2 User Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_User_Guide&amp;diff=2082"/>
		<updated>2026-07-07T06:51:51Z</updated>

		<summary type="html">&lt;p&gt;Admin: change image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Safety Information ==&lt;br /&gt;
{{Notice&lt;br /&gt;
| title = Installer Qualifications and Safety&lt;br /&gt;
| body = *LIGO SP Fuel Level Sensor measures the accurate fuel level in fuel tanks, therefore the device installation process also needs to be extremely accurate.&lt;br /&gt;
*Installation technicians should be well-trained by specialists or experts in this field before installing the device.&lt;br /&gt;
*Implement protection against electric shocks and electrical fires, labour safety and hygiene.&lt;br /&gt;
*List of installing tools is provided in section APPENDIX A below.&lt;br /&gt;
*List of accessories is provided in section APPENDIX B below.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Product Overview ==&lt;br /&gt;
LIGO Fuel level sensor is produced and developed by SOJI Electronics Join Stock Company. The device is designed to measure the level of liquid fuels and other non-conductive liquids in vehicle&#039;s tanks and stationary fuel storages, applicable in different fields. The measured values will be t transformed into output signal in the form of: Analog, Frequency, RS232, RS485… and transmitted to a connected external device&lt;br /&gt;
&lt;br /&gt;
Under particular conditions, the device can reach a high accuracy up to 99.5%. At present, on the market there are several lines of sensor using different technologies to measure fuel level such as: magnetically operated switches (reed switches), ultrasound wave (ultrasonic sensor), capacitive sensing (capacitive sensor). Among these ones, capacitive sensing technology is considered to have highest accuracy and greatest lifespan.&lt;br /&gt;
&lt;br /&gt;
== Package Contents ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! STT !! Description !! Qty&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | Ligo PRO 2 Fuel Level Sensor. Standard lengths: 700, 1000, and 1500mm (for other customized lengths, please contact the manufacturer) || 01&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | Oil filter || 01&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Gasoline resistant rubber gasket || 01&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Anti-vibration spring when moving || 01&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | 1A Fuse protection || 01&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Self-drilling screw M4.8x32mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Rivet and screw M5x20mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Sealing wire || 02&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | 7m signal wire || 01&lt;br /&gt;
|-&lt;br /&gt;
| 10 || style=&amp;quot;text-align:left;&amp;quot; | Quick installation manual || 01&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500 ... up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0-9V) || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || colspan=&amp;quot;3&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V) || colspan=&amp;quot;3&amp;quot; | 6-40V&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Supply protection (Over-voltage 100 VDC for 2 min. Reverse polarity) || colspan=&amp;quot;3&amp;quot; | Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40 ... +85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level, % || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output range (min level) || Analog (0...8V) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output range (max level) || Analog (1...9V) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 0 ... 255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, s || Continuous || 1 ... 255 || 1 ... 255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement, °C || colspan=&amp;quot;3&amp;quot; | ±1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.1, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity, +8 dBm TX Power&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || colspan=&amp;quot;3&amp;quot; | 10&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Selecting the location to install sensor ===&lt;br /&gt;
&lt;br /&gt;
* Installation location should be as close as possible to the geometric center and placed at the deepest level of the tank.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig1-location.png|frameless|894px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Selecting the location to install sensor&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When being installed, the sensor should not be in contact with reinforcement ribs inside the tank&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig2-two-sensors.png|frameless|730px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Installation of two sensors in one tank allows for significant reduction&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Drilling the hole on fuel tank ===&lt;br /&gt;
&lt;br /&gt;
* Drill out the central bore by bimetal core drill ø38 mm&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig3-drilling.png|frameless|580px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Drilling the selected hole&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Drill out four mounting holes according to the diagram:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig4-mounting-holes.png|frameless|541px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Drill out four mounting holes&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mounting hole diameter depends on the tank material:&lt;br /&gt;
&lt;br /&gt;
* ø 4 mm – for metal tank with wall thickness over 3 mm (cut M5 self screw M4.8)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig5-hole-thick.png|frameless|402px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. Hole thick&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ø 7 mm – for plastic and metal tank with wall thickness up to 3 mm (for rivets)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig6-rivet-nut.png|frameless|372px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 6. Rivet nut M5&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig7-install-rivet.png|frameless|535px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 7. Installing revit nut if tank&#039;s wall is thinner than 3mm&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Measuaring the tank depth and cutting sensor&#039;measuring probe ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig8-measure-depth.png|frameless|615px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 8. Measuring the tank depth&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Cut the sensor&#039;s measuring probe so that its length is 25 mm less than the depth of the tank. Ensure that shearing line is perpendicular to the sensor longitudinal axis&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig9-length.png|frameless|723px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 9. Origin length (L) and length after cutting (Lm)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes: Minimum allowable length of the measuring probe is 150 mm.&lt;br /&gt;
&lt;br /&gt;
=== Installing screen filter ===&lt;br /&gt;
Screen filter is mounted on the measuring probe of sensor to protect measuring probe electrodes from mud and water. Using the filter extends significantly faultless lifetime of the sensor.&lt;br /&gt;
&lt;br /&gt;
Screen filter mounting order: firstly put on fixator, then put on bottom stop and fix it with two side screws. Put the screen filter over bottom stop and fasten it with a fixator locks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig15-screen-filter.png|frameless|747px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 10. Installing screen filter procedue&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing and connecting ===&lt;br /&gt;
&lt;br /&gt;
* Applying oil-and-petrol resistant non-conductive sealant to ruber gasket&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig16-sealant.png|frameless|595px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 11. Applying the sealant to gasket&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Put the sensor into the tank and fix: when fixing with rivets, use a rivet driver when bolting, put on a seal (per bolt), a spacer and a spring washer when fixing to plastic tanks with wall thickness over 3 mm, use vendor furnished self-tapping screws and a seal (per self-tapping screw)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig17-install.png|frameless|663px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 12. Put sensor into the tank&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Connect the sensors to an external device using 7m cable&lt;br /&gt;
&lt;br /&gt;
The wire cable color:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RS232:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig3-wiring-RS232.png|frameless|801x801px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 13. Wiring diagram of RS232 and RS485 output signals&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | RXD&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | TXD&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 6 - 40 VDC&lt;br /&gt;
|}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RS485:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig4-wiring-RS485.png|frameless|801x801px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 14. Wiring diagram of RS232 and RS485 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | RXD/B-&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | TXD/A+&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 6 - 40 VDC&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AF:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig2-wiring-AF.png|frameless|873x873px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 15. Wiring diagram of Analog and Frequency output signals.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | Out (AF)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 6 - 40 VDC&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Connect the fuse holder to LIGO sensor power cable (brown wire) in close vicinity to the vehicle power supply circuit&lt;br /&gt;
* Installing the fuse in the fuse holder&lt;br /&gt;
&lt;br /&gt;
=== Sealing ===&lt;br /&gt;
&lt;br /&gt;
* Install the bolt through a hole in the seal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig22-sealing.png|frameless|891px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 16. Sealing&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== Connecting sensor to smartphone and setting ===&lt;br /&gt;
&lt;br /&gt;
To configure the LIGO AIR fuel sensor, download the &#039;&#039;&#039;LIGO BLE Configurator&#039;&#039;&#039; app on &#039;&#039;&#039;Appstore (for IOS) or CH Play (for Android devices).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hereinafter LIGO BLE Configurator is referred to as the &amp;quot;application&amp;quot; or &amp;quot;app&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; For full operation, you must grant all the permissions requested by the app&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-AIR-fig9-app-permissions-1.jpg|Figure 17. Grant all the permissions request by program&lt;br /&gt;
File:LIGO-AIR-fig10-app-permissions-2.jpg|Figure 18. Grant all the permissions request by program&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Grant all the permissions request by program as above&lt;br /&gt;
&lt;br /&gt;
==== Connecting sensor ====&lt;br /&gt;
&lt;br /&gt;
Run the application and select the sensor you want to connect to, as shown in the pictures below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-Connect-PRO-BLE-1.jpg|Figure 19. Selecting sensor type&lt;br /&gt;
File:LIGO-Connect-PRO-BLE-2.jpg|Figure 20. Connecting sensor&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set Empty and Set Full ====&lt;br /&gt;
&lt;br /&gt;
Step 1: Set Full&lt;br /&gt;
&lt;br /&gt;
* Immerse the sensor in fuel until the fuel reaches the sensor flange&lt;br /&gt;
* Wait for C_working value to stablize and then press Set Full button&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig13-set-full.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 21. Set Full&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 2: Set Empty&lt;br /&gt;
&lt;br /&gt;
* Remove the sensor from the fuel and wait for 1-2 minutes allow the fuel drain from the measuring probe and then press Set Empty button.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig14-set-empty.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 22. Set Empty&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Calibration table ===&lt;br /&gt;
&lt;br /&gt;
After installation, the sensor must be calibrated to ensure accurate volume measurement. Calibration table can be performed either on the platform (server) or locally on the sensor.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Platform (Server):&#039;&#039;&#039; The sensor sends raw level values (ranging from 0 to 4095) to the server via the telematic device. The platform uses a calibration table to convert these values into volume (liters).&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Sensor:&#039;&#039;&#039; The sensor directly sends volume values (liters) based on the internal calibration table.&lt;br /&gt;
&lt;br /&gt;
Note: For LIGO AIR sensors, the manufacturer recommends performing calibration table on the platform.&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Channel !! Detail&lt;br /&gt;
|-&lt;br /&gt;
| Company || SOJI ELECTRONICS.,JSC&lt;br /&gt;
|-&lt;br /&gt;
| Address || C50-LK13, Lot No. 09, New Urban Area, Le Trong Tan street, Duong Noi Ward, Ha Dong District, Hanoi City, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Tel/Fax || +84 24 62 932 369&lt;br /&gt;
|-&lt;br /&gt;
| Email || contact@sojielectronics.com&lt;br /&gt;
|-&lt;br /&gt;
| Website || [http://sojielectronics.com sojielectronics.com]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2 ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_ADAPTER_User_Guide&amp;diff=2081</id>
		<title>LIGO AIR ADAPTER User Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_ADAPTER_User_Guide&amp;diff=2081"/>
		<updated>2026-07-06T00:47:43Z</updated>

		<summary type="html">&lt;p&gt;Admin: change content&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Safety Information ==&lt;br /&gt;
{{Notice&lt;br /&gt;
| title = Important notes during sensor installation&lt;br /&gt;
| body = Read and understand all safety information in this document before installing, operating or servicing the LIGO AIR ADAPTER&lt;br /&gt;
*Keep the device out of the reach of children. It is not a toy and contains small parts.&lt;br /&gt;
*Do not modify, drill or otherwise alter the device.&lt;br /&gt;
*Always &#039;&#039;&#039;disconnect power&#039;&#039;&#039; before wiring, connecting/disconnecting cables or servicing.&lt;br /&gt;
*Connect the device only to a power source within the rated input voltage.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Product Overview ==&lt;br /&gt;
&lt;br /&gt;
=== Scope ===&lt;br /&gt;
&lt;br /&gt;
This document provides instructions for installation, configuration, operation, and basic troubleshooting of the LIGO AIR ADAPTER.&lt;br /&gt;
&lt;br /&gt;
It is intended for system integrators, installers, and technical personnel involved in fuel monitoring and GPS telematics systems.&lt;br /&gt;
&lt;br /&gt;
=== Product Overview ===&lt;br /&gt;
&lt;br /&gt;
LIGO AIR ADAPTER is a communication and aggregation product designed to connect Ligo Air/Ligo Air Pro wireless fuel level sensors to GPS trackers that do not support BLE communication.&lt;br /&gt;
&lt;br /&gt;
The device acts as:&lt;br /&gt;
* A BLE to wired protocol bridge.&lt;br /&gt;
* A Sensor Union Module (SUM) capable of aggregating data from multiple sensors.&lt;br /&gt;
* A BLE signal receiver with enhanced sensitivity for extended wireless range.&lt;br /&gt;
&lt;br /&gt;
LIGO AIR ADAPTER supports multiple output interfaces including RS232, RS485, and AF, ensuring compatibility with a wide range of GPS tracking devices.&lt;br /&gt;
&lt;br /&gt;
=== Wiring diagram ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:System architecture1.png|frameless|1150px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. LIGO AIR ADAPTER — Wiring diagram&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
* Wireless fuel sensors (Ligo Air / Ligo Air Pro) transmit fuel level data via BLE.&lt;br /&gt;
* LIGO AIR ADAPTER receives BLE data and processes it.&lt;br /&gt;
* Processed data is transmitted to the GPS tracker via RS232, RS485 or AF interface.&lt;br /&gt;
* Log sensor data for history view and troubleshooting.&lt;br /&gt;
&lt;br /&gt;
== Package Contents ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 1. LIGO AIR ADAPTER — Package content&lt;br /&gt;
!No.&lt;br /&gt;
!Description&lt;br /&gt;
!Qty (pcs)&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|LIGO AIR ADAPTER&lt;br /&gt;
|01&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|Quick user guide&lt;br /&gt;
|01&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|+ Table 2. LIGO AIR ADAPTER — Detail specifications&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&#039;&#039;&#039;No.&#039;&#039;&#039;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&#039;&#039;&#039;PARAMETERS&#039;&#039;&#039;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&#039;&#039;&#039;RS232&#039;&#039;&#039;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&#039;&#039;&#039;RS485&#039;&#039;&#039;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&#039;&#039;&#039;PRO&#039;&#039;&#039;&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |&#039;&#039;&#039;AF&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |1&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Dimension (without wire), mm&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; valign=&amp;quot;top&amp;quot; |67x80&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |2&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Output interface&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; valign=&amp;quot;top&amp;quot; |Digital&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |Analog &amp;amp; Digital&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |Analog&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |3&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Baud rate, bit/sec&lt;br /&gt;
| colspan=&amp;quot;3&amp;quot; valign=&amp;quot;top&amp;quot; |2400, 4800, 9600, 14400, 19200, 38400, 115200.&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; | &lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |4&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Power supply, VDC&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; valign=&amp;quot;top&amp;quot; |7 – 36 VDC&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |5&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Maximum power consumption&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; valign=&amp;quot;top&amp;quot; |~15 mA @13.5VDC&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |6&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Power supply protection&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; valign=&amp;quot;top&amp;quot; |1000VDC reverse polarity protection&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |7&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Operating temperature, °C&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; valign=&amp;quot;top&amp;quot; |-40 to 85 °C&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |8&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Waterproof standard&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; valign=&amp;quot;top&amp;quot; |IP67&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |9&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Output value range&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; valign=&amp;quot;top&amp;quot; |Digital&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |Digital and analog &lt;br /&gt;
&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |Analog (0-10V)&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |10&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Data output mode&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; valign=&amp;quot;top&amp;quot; |SUM, AVERAGE, INDIVIDUAL&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |11&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Bluetooth specs&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; valign=&amp;quot;top&amp;quot; |BLE 5.2&lt;br /&gt;
|-&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; |12&lt;br /&gt;
| valign=&amp;quot;top&amp;quot; style=&amp;quot;text-align:left;&amp;quot; |Average service life, years (expected)&lt;br /&gt;
| colspan=&amp;quot;4&amp;quot; valign=&amp;quot;top&amp;quot; |8 years&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Hardware description and installation ===&lt;br /&gt;
&lt;br /&gt;
==== Cable and wire definition ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|+ Table 3. LIGO AIR ADAPTER — Cable description&lt;br /&gt;
! No. !! Wire Color !! Wire Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f00;border:1px solid #999;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;RED&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;|| &#039;&#039;&#039;VCC&#039;&#039;&#039;|| style=&amp;quot;text-align:left;&amp;quot; | Power supply (7–36 V DC)&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#fff;border:1px solid #999;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;WHITE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;|| &#039;&#039;&#039;AF&#039;&#039;&#039;|| style=&amp;quot;text-align:left;&amp;quot; | Analog output (0–10 V)&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;BLACK&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;|| &#039;&#039;&#039;GND&#039;&#039;&#039;|| style=&amp;quot;text-align:left;&amp;quot; | Ground&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#2e8b2e;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;GREEN&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;|| &#039;&#039;&#039;RS485-B&#039;&#039;&#039;|| style=&amp;quot;text-align:left;&amp;quot; | Signal wire B for RS485&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;YELLOW&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;|| &#039;&#039;&#039;RS485-A&#039;&#039;&#039;|| style=&amp;quot;text-align:left;&amp;quot; | Signal wire A for RS485&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;BLACK&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;|| &#039;&#039;&#039;GND&#039;&#039;&#039;|| style=&amp;quot;text-align:left;&amp;quot; | Ground&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#7b3f00;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;BROWN&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;|| &#039;&#039;&#039;RS232-RX&#039;&#039;&#039;|| style=&amp;quot;text-align:left;&amp;quot; | Input data for RS232&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#1e6cff;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;BLUE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || &#039;&#039;&#039;RS232-TX&#039;&#039;&#039; || style=&amp;quot;text-align:left;&amp;quot; | Output data for RS232&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Mechanical installation ====&lt;br /&gt;
&lt;br /&gt;
* Install Ligo Adapter inside the vehicle cabin or protected enclosure&lt;br /&gt;
* Avoid direct exposure to water, excessive heat, or strong electromagnetic sources&lt;br /&gt;
* Ensure sufficient distance from high-current cables when possible&lt;br /&gt;
&lt;br /&gt;
==== Electrical connection ====&lt;br /&gt;
Connect the selected communication interface to the GPS tracker following the table below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|+ Table 4. LIGO AIR ADAPTER — Wiring connection&lt;br /&gt;
! No. !! Wire Name !! RS232 !! RS485 !! AF !! PRO&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &#039;&#039;&#039;VCC&#039;&#039;&#039; || ✓ || ✓ || ✓ || ✓&lt;br /&gt;
|-&lt;br /&gt;
| 2 || &#039;&#039;&#039;AF&#039;&#039;&#039; || — || — || ✓ || ✓&lt;br /&gt;
|-&lt;br /&gt;
| 3 || &#039;&#039;&#039;GND&#039;&#039;&#039; || ✓ || ✓ || ✓ || ✓&lt;br /&gt;
|-&lt;br /&gt;
| 4 || &#039;&#039;&#039;RS485-B&#039;&#039;&#039; || — || ✓ || — || ✓&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &#039;&#039;&#039;RS485-A&#039;&#039;&#039; || — || ✓ || — || ✓&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &#039;&#039;&#039;GND&#039;&#039;&#039; || ✓ || ✓ || ✓ || ✓&lt;br /&gt;
|-&lt;br /&gt;
| 7 || &#039;&#039;&#039;RS232-RX&#039;&#039;&#039; || ✓ || — || — || ✓&lt;br /&gt;
|-&lt;br /&gt;
| 8 || &#039;&#039;&#039;RS232-TX&#039;&#039;&#039; || ✓ || — || — || ✓&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Notice&lt;br /&gt;
| title = Output Protocol Compatibility — RS232 / RS485 / AF&lt;br /&gt;
| body = The PRO model supports all available output communication protocols (RS232, RS485 and AF). Select the output interface compatible with the existing GPS tracker.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
=== LED Status Indication ===&lt;br /&gt;
&lt;br /&gt;
The Ligo Adapter is equipped with a status LED to indicate its current operating state.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 5. LIGO AIR ADAPTER — LED status&lt;br /&gt;
! LED Status !! Description&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | Red blinking || No sensor is paired with the adapter&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | Red solid || Sensor(s) paired, but no data is being received&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | Green solid || Valid data is being received from paired sensor(s)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== Software configuration ===&lt;br /&gt;
&lt;br /&gt;
LIGO AIR ADAPTER is configured using Ligo BLE Configurator. Please download and install it from Play Store on android OS or App Store on IOS before starting configurations.&lt;br /&gt;
&lt;br /&gt;
The software is structured into three main sections, each serving a specific purpose in the installation, operation and troubleshooting as these below figures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-BLE-Adapter-fig2-connection-section.png|Figure 2. Connection section&lt;br /&gt;
File:LIGO-BLE-Adapter-fig3-settings-section.png|Figure 3. Settings section&lt;br /&gt;
File:LIGO-BLE-Adapter-fig4-help-section.png|Figure 4. Help section&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; gap:30px; flex-wrap:wrap; align-items:flex-start;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;flex:1; min-width:260px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size:1.2em; font-weight:bold; color:#0054A6; border-bottom:2px solid #eaecf0; padding-bottom:5px; margin-bottom:10px;&amp;quot;&amp;gt;Connection section&amp;lt;/div&amp;gt;&lt;br /&gt;
This section provides general information and real-time status of the device. It allows users to quickly verify that the adapter is powered, accessible, and ready for configuration.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Included information:&#039;&#039;&#039;&lt;br /&gt;
* Device name&lt;br /&gt;
* Device MAC address and serial number&lt;br /&gt;
* Firmware / Hardware version&lt;br /&gt;
* Fuel data of paired sensors&lt;br /&gt;
* Supply voltage and temperature&lt;br /&gt;
* RSSI signal&lt;br /&gt;
&lt;br /&gt;
The Connection section allows users to quickly verify that the adapter is powered, accessible, and ready for configuration.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;flex:1; min-width:260px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size:1.2em; font-weight:bold; color:#0054A6; border-bottom:2px solid #eaecf0; padding-bottom:5px; margin-bottom:10px;&amp;quot;&amp;gt;Settings section&amp;lt;/div&amp;gt;&lt;br /&gt;
The Settings section contains all configuration parameters related to sensor pairing, output interfaces, and data type. It is intended for device setup and integration with GPS tracking devices.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Key configuration functions:&#039;&#039;&#039;&lt;br /&gt;
* Sensor pairing management (add / remove sensors using MAC address)&lt;br /&gt;
* Output interface configuration (RS232, RS485, AF)&lt;br /&gt;
* Communication protocol&lt;br /&gt;
* Data mode selection (SUM, AVERAGE, INDIVIDUAL)&lt;br /&gt;
* Data log configuration and viewing&lt;br /&gt;
* Installation report generation&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;flex:1; min-width:260px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size:1.2em; font-weight:bold; color:#0054A6; border-bottom:2px solid #eaecf0; padding-bottom:5px; margin-bottom:10px;&amp;quot;&amp;gt;Help section&amp;lt;/div&amp;gt;&lt;br /&gt;
The Support section provides technical assistance and software information.&lt;br /&gt;
&lt;br /&gt;
This section includes:&lt;br /&gt;
* Software version&lt;br /&gt;
* Technical support contact information&lt;br /&gt;
* Reference documentation and troubleshooting resources&lt;br /&gt;
&lt;br /&gt;
This section ensures that users can easily access assistance if necessary.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
=== Sensor pairing and configuration ===&lt;br /&gt;
&lt;br /&gt;
==== Sensor pairing ====&lt;br /&gt;
&lt;br /&gt;
Follow these steps to pair Ligo Air/Ligo Air Pro sensors with LIGO AIR ADAPTER&lt;br /&gt;
&lt;br /&gt;
* Launch Ligo BLE Configurator application &amp;gt; Select LIGO AIR ADAPTER (the software auto scan nearby BLE Adapter) &amp;gt; press Connect button&lt;br /&gt;
* Switch to Settings section to start pairing sensor to the LIGO AIR ADAPTER as Figure 4.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-Adapter-config-select-device.png|Step 1. Select device&lt;br /&gt;
File:LIGO-Adapter-config-scan-sensor.png|Step 2. Scan sensors&lt;br /&gt;
File:LIGO-Adapter-config-confirm-pairing.png|Step 3. Confirm pairing&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Parameters configuration ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-Adapter-parameter-1.png|Parameters - page 1&lt;br /&gt;
File:LIGO-Adapter-parameter-2.png|Parameters - page 2&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! No. !! Parameter !! Description&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 1 || Message interval (1..60s) || Defines the time interval between two consecutive data transmissions from the LIGO AIR ADAPTER&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 2 || Protocol || Selects the output communication protocol used to transmit fuel data to the GPS tracker.&lt;br /&gt;
* SOJI protocol&lt;br /&gt;
* LLS protocol&lt;br /&gt;
|+ Table 6. LIGO AIR ADAPTER — Parameter descriptions&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 3 || Automatic transmission mode || Defines the data transmission format.&lt;br /&gt;
* HEX format&lt;br /&gt;
* ASCII format&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 4 || Baud rate || Sets the serial communication speed for RS232 or RS485 output.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 5 || Data mode || Defines how fuel data from multiple paired sensors is processed before transmission.&lt;br /&gt;
* SUM: Total fuel value of all paired sensors&lt;br /&gt;
* AVERAGE: Average fuel value of all paired sensors&lt;br /&gt;
* INDIVIDUAL: Fuel data from each sensor is transmitted individually upon Modbus request.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 6 || Address (1-254) || Sets the Modbus address of the LIGO AIR ADAPTER.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 7 || Level min (0..8V) || Defines the minimum output voltage level for the AF output.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 8 || Level max (2..10V) || Defines the maximum output voltage level for the AF output.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 9 || Max volume || Defines the maximum digital fuel level resolution used for data transmission.&lt;br /&gt;
* 1023: 10-bit resolution.&lt;br /&gt;
* 4095: 12-bit resolution.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Operation ==&lt;br /&gt;
&lt;br /&gt;
=== Data log and settings log ===&lt;br /&gt;
&lt;br /&gt;
==== Data log ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-Adapter-open-log.png|Step 1. Open log&lt;br /&gt;
File:LIGO-Adapter-select-period.png|Step 2. Select period&lt;br /&gt;
File:LIGO-Adapter-view-data.png|Step 3. View data&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Data Log function allows users to view historical fuel level data received from paired sensors over time.&lt;br /&gt;
&lt;br /&gt;
The log records:&lt;br /&gt;
* Timestamp of received data&lt;br /&gt;
* Fuel level values from paired sensors&lt;br /&gt;
* RSSI signal&lt;br /&gt;
* Temperature of sensor&lt;br /&gt;
&lt;br /&gt;
This function is used for:&lt;br /&gt;
* System verification during installation&lt;br /&gt;
* Diagnostic and troubleshooting purposes&lt;br /&gt;
* Validation of sensor communication stability&lt;br /&gt;
&lt;br /&gt;
The user can select a time period (Today, Yesterday, Week, Month, or a Custom Period) and then press the Download button to retrieve the data log, as shown in Figure 6.&lt;br /&gt;
&lt;br /&gt;
The retrieved data log is displayed in the Log Display Area.&lt;br /&gt;
&lt;br /&gt;
==== Settings log ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-Adapter-open-setting-log.png|Step 1. Open settings log&lt;br /&gt;
File:LIGO-Adapter-select-source.png|Step 2. Select source&lt;br /&gt;
File:LIGO-Adapter-view-setting-log.png|Step 3. View log&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Settings Log function allows users to review the historical configuration records of the LIGO AIR ADAPTER.&lt;br /&gt;
&lt;br /&gt;
This log stores all parameter changes previously configured by the user, enabling traceability and verification of device setup.&lt;br /&gt;
&lt;br /&gt;
The user can select the Log source (data from EEPROM or data on the server), and the settings log will be displayed in the Log Display Area, as shown in Figure 7.&lt;br /&gt;
&lt;br /&gt;
==== Report ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; gap:30px; align-items:flex-start; flex-wrap:wrap;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;flex:1; min-width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size:1.1em; font-weight:bold; color:#0054A6;&amp;quot;&amp;gt;Installation Report&amp;lt;/div&amp;gt;&lt;br /&gt;
Each installation report includes:&lt;br /&gt;
*Installer Name&lt;br /&gt;
*Installation Date and Time&lt;br /&gt;
*Serial Number of Adapter (SN)&lt;br /&gt;
*Model: Vehicle Information&lt;br /&gt;
*Plate number: License of vehicle&lt;br /&gt;
*Other information&lt;br /&gt;
*Real installation photos captured via mobile device&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:install-report.png|frameless|284px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 9. Installation report&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; gap:30px; align-items:flex-start; flex-wrap:wrap;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;flex:1; min-width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size:1.1em; font-weight:bold; color:#0054A6;&amp;quot;&amp;gt;Maintenance Report&amp;lt;/div&amp;gt;&lt;br /&gt;
Each maintenance report includes:&lt;br /&gt;
*Installer Name&lt;br /&gt;
*Maintenance Date and Time&lt;br /&gt;
*Serial Number of Adapter (SN)&lt;br /&gt;
*Model: Vehicle Information&lt;br /&gt;
*Plate number: License of vehicle&lt;br /&gt;
*Other information&lt;br /&gt;
*Real installation photos captured via mobile device&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:maintance-report.png|frameless|284px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 10. Maintenance report&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex; gap:30px; align-items:flex-start; flex-wrap:wrap;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;flex:1; min-width:280px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-size:1.1em; font-weight:bold; color:#0054A6;&amp;quot;&amp;gt;View Report&amp;lt;/div&amp;gt;&lt;br /&gt;
The View Report function allows users to review installation and maintenance history at any time.&lt;br /&gt;
&lt;br /&gt;
Steps to open reports:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Open View Report&#039;&#039;&#039; in the Ligo BLE Configurator App.&lt;br /&gt;
* &#039;&#039;&#039;Enter the Adapter Serial Number (SN)&#039;&#039;&#039; and press the Search button.&lt;br /&gt;
&lt;br /&gt;
The system displays:&lt;br /&gt;
*Installation reports&lt;br /&gt;
*Maintenance reports&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:view-report.png|frameless|284px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 11. View report&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 7. LIGO AIR ADAPTER — Troubleshooting&lt;br /&gt;
! Issue !! Possible Cause !! Recommended Action&lt;br /&gt;
|-&lt;br /&gt;
| No data on GPS || Incorrect wiring || Verify interface wiring&lt;br /&gt;
|-&lt;br /&gt;
| Sensor not detected || MAC address not added, wrong MAC address || Reconfigure sensor list&lt;br /&gt;
|-&lt;br /&gt;
| Unstable data || BLE signal attenuation || Reposition adapter&lt;br /&gt;
|-&lt;br /&gt;
| Incorrect fuel value || Wrong data mode || Check SUM / AVERAGE setting&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Certification / Compliance ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[LIGO AIR ADAPTER Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Table 9. LIGO AIR ADAPTER — Support/ Contact&lt;br /&gt;
! Channel !! Detail&lt;br /&gt;
|-&lt;br /&gt;
| Company || SOJI ELECTRONICS.,JSC&lt;br /&gt;
|-&lt;br /&gt;
| Address || C50-LK13, Lot No. 09, New Urban Area, Le Trong Tan street, Duong Noi Ward, Ha Dong District, Hanoi City, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Tel/Fax || +84 24 62 932 369&lt;br /&gt;
|-&lt;br /&gt;
| Email || contact@sojielectronics.com&lt;br /&gt;
|-&lt;br /&gt;
| Website || [http://sojielectronics.com sojielectronics.com]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ADAPTER ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_ADAPTER_Datasheet&amp;diff=2080</id>
		<title>LIGO AIR ADAPTER Datasheet</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_ADAPTER_Datasheet&amp;diff=2080"/>
		<updated>2026-07-06T00:44:30Z</updated>

		<summary type="html">&lt;p&gt;Admin: change Image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Product Overview ==&lt;br /&gt;
&#039;&#039;&#039;Ligo BLE Adapter&#039;&#039;&#039; is an intelligent communication and integration module designed to enable seamless connectivity between &#039;&#039;&#039;wireless fuel level sensors (BLE)&#039;&#039;&#039; and &#039;&#039;&#039;GPS/telematics devices that do not natively support BLE communication&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Acting as a protocol bridge, Ligo Adapter allows existing GPS systems to integrate wireless fuel sensors without hardware modification or replacement, significantly reducing deployment and upgrading costs while enhancing system flexibility.&lt;br /&gt;
&lt;br /&gt;
In addition to protocol conversion, Ligo Adapter functions as a &#039;&#039;&#039;SUM Data&#039;&#039;&#039;, capable of simultaneously connecting to &#039;&#039;&#039;10 wireless fuel sensors&#039;&#039;&#039; and aggregating their data for transmission to the GPS device via standard wired interfaces such as &#039;&#039;&#039;RS232, RS485, and Analog/Frequency (AF).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To ensure reliable wireless performance in challenging environments, Ligo Adapter incorporates a &#039;&#039;&#039;BLE receive-path signal amplification mechanism&#039;&#039;&#039;, improving receiver sensitivity and extending the effective communication range of wireless fuel sensors. This feature is particularly beneficial in environments with high attenuation or electromagnetic interference, such as vehicle cabins, metal fuel tanks, and industrial machinery.&lt;br /&gt;
&lt;br /&gt;
== Key Features ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Protocol&#039;&#039;&#039; &#039;&#039;&#039;bridge for Non-BLE GPS/Telematics&#039;&#039;&#039;: Enables seamless connectivity between &#039;&#039;&#039;wireless BLE fuel level sensors&#039;&#039;&#039; and &#039;&#039;&#039;GPS/telematics devices that do not natively support BLE&#039;&#039;&#039;.&lt;br /&gt;
* &#039;&#039;&#039;Multi-Sensor&#039;&#039;&#039; &#039;&#039;&#039;data hub (up to 10 Sensors)&#039;&#039;&#039;: Connecting to up to &#039;&#039;&#039;10 wireless fuel sensors simultaneously.&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Standard wired output interfaces:&#039;&#039;&#039; Transmits aggregated sensor data to the GPS/telematics device via standard wired interfaces &#039;&#039;&#039;RS232, RS485, AF.&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;BLE receive-path signal amplification:&#039;&#039;&#039; Incorporates a &#039;&#039;&#039;BLE receive-path signal amplification&#039;&#039;&#039; mechanism that improves &#039;&#039;&#039;receiver sensitivity&#039;&#039;&#039; and &#039;&#039;&#039;extends the effective communication range&#039;&#039;&#039; of wireless fuel sensors.&lt;br /&gt;
&lt;br /&gt;
== Applications ==&lt;br /&gt;
&lt;br /&gt;
Ligo Adapter is developed to achieve the following objectives:&lt;br /&gt;
&lt;br /&gt;
* Allows GPS and telematics units without BLE capability to communicate with wireless fuel level sensors.&lt;br /&gt;
* Support multiple wired communication interfaces. Ensures broad compatibility with a wide range of GPS devices through RS232, RS485, and AF interfaces.&lt;br /&gt;
* Aggregate data from multiple sensors (SUM function). Supports simultaneous connection of up to 10 fuel level sensors, consolidating sensor data into a single communication channel to the GPS unit.&lt;br /&gt;
* Extend BLE communication range and reliability. Enhances BLE reception through signal amplification, improving data stability and transmission distance in high-loss or noisy environments.&lt;br /&gt;
&lt;br /&gt;
== Product Variants / Ordering Information ==&lt;br /&gt;
&lt;br /&gt;
== Technical Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! No. !! Parameters !! RS232 !! RS485 !! PRO !! AF&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | Dimention (without wire), mm || 67x80&lt;br /&gt;
|67x80&lt;br /&gt;
|67x80&lt;br /&gt;
|67x80&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | Output interface || Digital &lt;br /&gt;
|Digital|| Analog &amp;amp; Digital || Analog&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || 2400–115200 &lt;br /&gt;
|2400–115200&lt;br /&gt;
|2400–115200||2400–115200&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Power supply, VDC || 7–36 VDC&lt;br /&gt;
|7–36 VDC&lt;br /&gt;
|7–36 VDC&lt;br /&gt;
|7–36 VDC&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption || ~15mA @13.5V&lt;br /&gt;
|~15mA @13.5V&lt;br /&gt;
|~15mA @13.5V&lt;br /&gt;
|~15mA @13.5V&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Power supply protection || 1000VDC reverse polarity|| 1000VDC reverse polarity|| 1000VDC reverse polarity||1000VDC reverse polarity&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || -40 to 85°C|| -40 to 85°C|| -40 to 85°C||-40 to 85°C&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard || IP67|| IP67|| IP67||IP67&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | Output signal type || Digital &lt;br /&gt;
|Digital|| Digital &amp;amp; Analog || Analog (0-10V)&lt;br /&gt;
|-&lt;br /&gt;
| 10 || style=&amp;quot;text-align:left;&amp;quot; | Data output mode || SUM / AVG / Individual&lt;br /&gt;
|SUM / AVG / Individual&lt;br /&gt;
|SUM / AVG / Individual&lt;br /&gt;
|SUM / AVG&lt;br /&gt;
|-&lt;br /&gt;
| 11 || style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || BLE 5.2|| BLE 5.2|| BLE 5.2||BLE 5.2&lt;br /&gt;
|-&lt;br /&gt;
| 12 || style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || 8 years|| 8 years|| 8 years||8 years&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Wiring Description ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Phương tiện truyền thông (15).jpg|frameless|466x466px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Wiring diagram of Ligo BLE Adapter&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Color !! Function !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f00;border:1px solid #999;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;RED&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || VBAT || style=&amp;quot;text-align:left;&amp;quot; | 7 - 36VDC&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#fff;border:1px solid #999;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;WHITE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || AF || style=&amp;quot;text-align:left;&amp;quot; | Analog output&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;BLACK&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || GND || style=&amp;quot;text-align:left;&amp;quot; | GND&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#2e8b2e;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;GREEN&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || RS485-B || style=&amp;quot;text-align:left;&amp;quot; | RS485-B&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;YELLOW&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || RS485-A || style=&amp;quot;text-align:left;&amp;quot; | RS485-A&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;BLACK&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || GND || style=&amp;quot;text-align:left;&amp;quot; | GND&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#7b3f00;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;BROWN&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || RS232-Rx || style=&amp;quot;text-align:left;&amp;quot; | RS232-Rx&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#1e6cff;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;BLUE&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;|| RS232-Tx || style=&amp;quot;text-align:left;&amp;quot; | RS232-Tx&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Compliance and Certifications ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[LIGO BLE ADAPTER Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ADAPTER ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=File:Ph%C6%B0%C6%A1ng_ti%E1%BB%87n_truy%E1%BB%81n_th%C3%B4ng_(15).jpg&amp;diff=2079</id>
		<title>File:Phương tiện truyền thông (15).jpg</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=File:Ph%C6%B0%C6%A1ng_ti%E1%BB%87n_truy%E1%BB%81n_th%C3%B4ng_(15).jpg&amp;diff=2079"/>
		<updated>2026-07-06T00:43:26Z</updated>

		<summary type="html">&lt;p&gt;Admin: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;upload&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Protocol&amp;diff=2078</id>
		<title>LIGO PRO 2 Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Protocol&amp;diff=2078"/>
		<updated>2026-06-30T11:56:29Z</updated>

		<summary type="html">&lt;p&gt;Admin: change content&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LLS Protocol is used in LIGO RS232 / RS485 fuel level sensor with the following parameters:&lt;br /&gt;
&lt;br /&gt;
* Baud rate: 2400,4800,9600,19200,38400,115200 (can be configured by software on PC or via LIGO BLE Configurator application on mobile phone)&lt;br /&gt;
* Data bits: 8&lt;br /&gt;
* Parity: None&lt;br /&gt;
* Stop bits: 1&lt;br /&gt;
* Flow control: None&lt;br /&gt;
&lt;br /&gt;
LIGO fuel level sensor has 2 working modes:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Slave mode:&#039;&#039;&#039; In this mode, the sensor will respond to all requests from the external device (master device). Each sensor in the network will be distinguished by different addresses configured with the LIGO Configurator software&lt;br /&gt;
# &#039;&#039;&#039;Master mode:&#039;&#039;&#039; In this mode the sensor will automatically send data to the external device with a preset interval (message interval)&lt;br /&gt;
&lt;br /&gt;
To enable this mode on the LIGO Configurator software, do the following:&lt;br /&gt;
&lt;br /&gt;
* Automatic transmission: select HEX (binary) or ASCII or ASCII EXT&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Automatic-Transmission.png|frameless|219px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Automatic transmission setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Set the time of each transfer (Message interval)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Message-Interval.png|frameless|228px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Message interval setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Exchange Protocol Description ==&lt;br /&gt;
&lt;br /&gt;
The LLS protocol supports two types of exchange protocols: binary (HEX) and character view (transmission of ASCII sequences). It is recommended to use binary exchange protocol.&lt;br /&gt;
&lt;br /&gt;
=== Format of Binary Protocol Messages ===&lt;br /&gt;
&lt;br /&gt;
All the commands of the binary communication protocol have the same standardized format which is given in the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequential number of the field !! Field name !! Field size, byte !! Description&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 1 || Prefix || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is a marker of the message beginning shall have prefix 31h, and an outcoming messages shall be displayed with 3Eh prefix by the program.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 2 || Network address || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the network address of the recipient. &amp;lt;br&amp;gt;For prefix 3Eh specify the network address of the sender.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 3 || Operation code || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the code of operation which the program shall perform For Eh prefix specify the code of operation to which the response is given&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 4 || Data || style=&amp;quot;text-align:center;&amp;quot; | It depends on the operation code || Data composition and format of the field depends on the operation code&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 5 || Checksum || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is used to control over data integrity&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Single-Stage Data Reading (command 06h) ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The data are transmitted with a lower byte ahead.&lt;br /&gt;
&lt;br /&gt;
==== Command format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output (command 07h) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to switch on periodic data output.&lt;br /&gt;
&lt;br /&gt;
After the command is processed, the sensor starts sending data periodically — level, temperature, and frequency — with the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power power supply (if the data output mode is not istalled by default).&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Periodic data output format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output Interval Adjustment (13h command) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to set up interval of periodic data output.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 0…255 || style=&amp;quot;text-align:left;&amp;quot; | Interval of the data output in seconds&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Default Data Output Mode (command 17h) ===&lt;br /&gt;
&lt;br /&gt;
This command determines the order of data output after the sensor is powered on or the processor is reset. After the power is on or the processor is reset, the program will send data periodically via the interface at the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | +3 || rowspan=&amp;quot;4&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in binary form&lt;br /&gt;
|-&lt;br /&gt;
| 02h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII mode)&lt;br /&gt;
|-&lt;br /&gt;
| 03h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII EXT mode)&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h…FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Description of Commands for the Text-Based Protocol ==&lt;br /&gt;
&lt;br /&gt;
Data exchange via the text-based protocol includes receipt and sending of ASCII symbols sequence interpreted and the request and response commands.&lt;br /&gt;
&lt;br /&gt;
=== Reading the Data ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The command is a sequence of symbols ASCII &amp;quot;D&amp;quot; and &amp;quot;O&amp;quot;. After receipt of the &amp;quot;DO&amp;quot; command the program will response in the form of ASCII symbols sequence.&lt;br /&gt;
&lt;br /&gt;
For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. All values are in hexadecimal form.&lt;br /&gt;
&lt;br /&gt;
In case the frequency value exceeds FFFh, the data are considered invalid.&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output ===&lt;br /&gt;
&lt;br /&gt;
The command is designed to switch on periodic data output. After processing the command, the sensor performs periodic data output in the text-based form (ASCII codes) of the following data: relative level, temperature, frequency.&lt;br /&gt;
&lt;br /&gt;
The data are being output periodically with an interval set up when cofiguring the sensor (LIGO Configurator software). In case the data output interval is set to zero, the data output won&#039;t be performed.&lt;br /&gt;
&lt;br /&gt;
Switching on of the periodic data output is done by sending of the &amp;quot;DP&amp;quot; symbols in line. After processing of the command, the symbols line will be received. For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power supply.&lt;br /&gt;
&lt;br /&gt;
== Checksum Calculation Algorithm ==&lt;br /&gt;
&lt;br /&gt;
The checksum is calculated using Dallas APPLICATION NOTE 27 table method: &#039;&#039;Understanding and Using Cyclic Redundancy Checks with Dallas Semiconductor iButton Products&#039;&#039;. One can use the following algorithms to calculate the checksum with a polynom &amp;lt;code&amp;gt;x^8 + x^5 + x^4 + 1&amp;lt;/code&amp;gt; (C language):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
U8 CRC8(U8 data, U8 crc)&lt;br /&gt;
{&lt;br /&gt;
    U8 i = data ^ crc;&lt;br /&gt;
    crc = 0;&lt;br /&gt;
    if (i &amp;amp; 0x01) crc ^= 0x5e;&lt;br /&gt;
    if (i &amp;amp; 0x02) crc ^= 0xbc;&lt;br /&gt;
    if (i &amp;amp; 0x04) crc ^= 0x61;&lt;br /&gt;
    if (i &amp;amp; 0x08) crc ^= 0xc2;&lt;br /&gt;
    if (i &amp;amp; 0x10) crc ^= 0x9d;&lt;br /&gt;
    if (i &amp;amp; 0x20) crc ^= 0x23;&lt;br /&gt;
    if (i &amp;amp; 0x40) crc ^= 0x46;&lt;br /&gt;
    if (i &amp;amp; 0x80) crc ^= 0x8c;&lt;br /&gt;
    return crc;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Modbus RTU Register Mapping ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The SOJI sensor is capable of communication via the RS-485 serial bus in the Modbus/RTU protocol. The sensor supports the Modbus function 03 – Read Holding Registers&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! No !! Address &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Description&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Unit&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x00 &lt;br /&gt;
|U16&lt;br /&gt;
|Sensor address (slave ID) &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 0x01 &lt;br /&gt;
|U16&lt;br /&gt;
|Min calibration (Cmin) – &#039;&#039;&#039;Low word&#039;&#039;&#039; &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
| 3 ||0x02 &lt;br /&gt;
|U16&lt;br /&gt;
|Min calibration (Cmin) – &#039;&#039;&#039;High word&#039;&#039;&#039; &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
| 4 &lt;br /&gt;
|0x03&lt;br /&gt;
|U16&lt;br /&gt;
|Fuel level data (12-bit / mm / liter) &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |raw / mm / L&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0x04&lt;br /&gt;
|U16&lt;br /&gt;
|Temperature &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |°C&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0x05&lt;br /&gt;
|U16&lt;br /&gt;
|Reserved &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0x06&lt;br /&gt;
|U16&lt;br /&gt;
|Reserved &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
| 8 ||0x07 &lt;br /&gt;
|U16&lt;br /&gt;
|Max calibration (Cmax) – &#039;&#039;&#039;Low word&#039;&#039;&#039; &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0x08&lt;br /&gt;
|U16&lt;br /&gt;
|Max calibration (Cmax) – &#039;&#039;&#039;High word&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0x09&lt;br /&gt;
|U16&lt;br /&gt;
|Sensor height (actual height, set via app)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot; |mm&lt;br /&gt;
|-&lt;br /&gt;
|11&lt;br /&gt;
|0x0A&lt;br /&gt;
|U16&lt;br /&gt;
|OSC current&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
|12&lt;br /&gt;
|0x0B&lt;br /&gt;
|U16&lt;br /&gt;
|OSC current&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot; |–&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;&#039;Detailed register description&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x00 — Sensor address&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The slave address of the sensor. The master uses this value as the address byte in the request frame.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x01 + 0x02 — Min calibration (Cmin) (32-bit)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;minimum&#039;&#039;&#039; calibration value is a 32-bit value split across two 16-bit registers:&lt;br /&gt;
&lt;br /&gt;
* 0x01 = &#039;&#039;&#039;Low     word&#039;&#039;&#039; (lower 16 bits)&lt;br /&gt;
* 0x02 = &#039;&#039;&#039;High     word&#039;&#039;&#039; (upper 16 bits)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Combination formula:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Cmin = (RegHigh &amp;lt;&amp;lt; 16) | RegLow&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x07 + 0x08 — Max calibration (Cmax) (32-bit)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same scheme as min, this is the &#039;&#039;&#039;maximum&#039;&#039;&#039; calibration value:&lt;br /&gt;
&lt;br /&gt;
* 0x07 =  Low word&lt;br /&gt;
* 0x08 =  High word&lt;br /&gt;
&lt;br /&gt;
Together, the min/max pairs define the &#039;&#039;&#039;calibration range&#039;&#039;&#039; of the capacitive sensor:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Empty&#039;&#039;&#039; tank ↔ Cmin&lt;br /&gt;
* &#039;&#039;&#039;Full&#039;&#039;&#039; tank ↔ Cmax&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x03 — Fuel level data&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
This register has &#039;&#039;&#039;3 output modes&#039;&#039;&#039;, selectable by the user through the &#039;&#039;&#039;mobile configuration app:&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Mode&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Value range&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Meaning&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Conversion&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Raw (12-bit)&#039;&#039;&#039;&lt;br /&gt;
|0 – 4095&lt;br /&gt;
|Raw value&lt;br /&gt;
|0 = empty, 4095 = full (per min/max calibration)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;mm (height)&#039;&#039;&#039;&lt;br /&gt;
|0 – (Sensor_height × 10)&lt;br /&gt;
|Fuel level height by actual height&lt;br /&gt;
|Actual level (mm) = Value / 10&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;3. Liter (volume)&#039;&#039;&#039;&lt;br /&gt;
|0 – Vmax&lt;br /&gt;
|Fuel volume&lt;br /&gt;
|Per the calibration table/factor configured in the app&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;&#039;Raw mode example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reg = 2048  →  2048 / 4095 ≈ 50% of full scale&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mm mode example&#039;&#039;&#039; (output = Fuel level height × 10):&lt;br /&gt;
&lt;br /&gt;
Reg = 3500  →  Fuel level height = 3500 / 10 = 350.0 mm&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Liter mode example:&#039;&#039;&#039; depends on tank geometry + the calibration chart the user enters in the app, e.g. Reg = 80 → 80 liters.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x04 — Temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature measured at the sensor (°C).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039; Reg = 25 → 25 °C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹 0x09 — Sensor height (actual probe height)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The physical height of the sensor probe, &#039;&#039;&#039;set by the user via the app&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reg  = 5000  → Sensor height = 5000/10 = 500 mm&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x0A + 0x0B — OSC current&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Oscillator current/parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x05, 0x06 — Reserved&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reserved for future expansion, currently unused.&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Protocol&amp;diff=2077</id>
		<title>LIGO PRO BLE Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Protocol&amp;diff=2077"/>
		<updated>2026-06-30T11:54:30Z</updated>

		<summary type="html">&lt;p&gt;Admin: change content&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LLS Protocol is used in LIGO RS232 / RS485 fuel level sensor with the following parameters:&lt;br /&gt;
&lt;br /&gt;
* Baud rate: 2400,4800,9600,19200,38400,115200 (can be configured by software on PC)&lt;br /&gt;
* Data bits: 8&lt;br /&gt;
* Parity: None&lt;br /&gt;
* Stop bits: 1&lt;br /&gt;
* Flow control: None&lt;br /&gt;
&lt;br /&gt;
LIGO fuel level sensor has 2 working modes:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Slave mode:&#039;&#039;&#039; In this mode, the sensor will respond to all requests from the external device (master device). Each sensor in the network will be distinguished by different addresses configured with the LIGO Configurator software&lt;br /&gt;
# &#039;&#039;&#039;Master mode:&#039;&#039;&#039; In this mode the sensor will automatically send data to the external device with a preset interval (message interval)&lt;br /&gt;
&lt;br /&gt;
To enable this mode on the LIGO Configurator software, do the following:&lt;br /&gt;
&lt;br /&gt;
* Automatic transmission: select HEX (binary) or ASCII or ASCII EXT&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Automatic-Transmission.png|frameless|219px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Automatic transmission setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Set the time of each transfer (Message interval)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Message-Interval.png|frameless|228px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Message interval setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Exchange Protocol Description ==&lt;br /&gt;
&lt;br /&gt;
The LLS protocol supports two types of exchange protocols: binary (HEX) and character view (transmission of ASCII sequences). It is recommended to use binary exchange protocol.&lt;br /&gt;
&lt;br /&gt;
=== Format of Binary Protocol Messages ===&lt;br /&gt;
&lt;br /&gt;
All the commands of the binary communication protocol have the same standardized format which is given in the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequential number of the field !! Field name !! Field size, byte !! Description&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 1 || Prefix || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is a marker of the message beginning shall have prefix 31h, and an outcoming messages shall be displayed with 3Eh prefix by the program.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 2 || Network address || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the network address of the recipient. &amp;lt;br&amp;gt;For prefix 3Eh specify the network address of the sender.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 3 || Operation code || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the code of operation which the program shall perform For Eh prefix specify the code of operation to which the response is given&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 4 || Data || style=&amp;quot;text-align:center;&amp;quot; | It depends on the operation code || Data composition and format of the field depends on the operation code&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 5 || Checksum || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is used to control over data integrity&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Single-Stage Data Reading (command 06h) ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The data are transmitted with a lower byte ahead.&lt;br /&gt;
&lt;br /&gt;
==== Command format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output (command 07h) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to switch on periodic data output.&lt;br /&gt;
&lt;br /&gt;
After the command is processed, the sensor starts sending data periodically — level, temperature, and frequency — with the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power power supply (if the data output mode is not istalled by default).&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Periodic data output format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output Interval Adjustment (13h command) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to set up interval of periodic data output.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 0…255 || style=&amp;quot;text-align:left;&amp;quot; | Interval of the data output in seconds&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Default Data Output Mode (command 17h) ===&lt;br /&gt;
&lt;br /&gt;
This command determines the order of data output after the sensor is powered on or the processor is reset. After the power is on or the processor is reset, the program will send data periodically via the interface at the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | +3 || rowspan=&amp;quot;4&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in binary form&lt;br /&gt;
|-&lt;br /&gt;
| 02h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII mode)&lt;br /&gt;
|-&lt;br /&gt;
| 03h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII EXT mode)&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h…FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Description of Commands for the Text-Based Protocol ==&lt;br /&gt;
&lt;br /&gt;
Data exchange via the text-based protocol includes receipt and sending of ASCII symbols sequence interpreted and the request and response commands.&lt;br /&gt;
&lt;br /&gt;
=== Reading the Data ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The command is a sequence of symbols ASCII &amp;quot;D&amp;quot; and &amp;quot;O&amp;quot;. After receipt of the &amp;quot;DO&amp;quot; command the program will response in the form of ASCII symbols sequence.&lt;br /&gt;
&lt;br /&gt;
For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. All values are in hexadecimal form.&lt;br /&gt;
&lt;br /&gt;
In case the frequency value exceeds FFFh, the data are considered invalid.&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output ===&lt;br /&gt;
&lt;br /&gt;
The command is designed to switch on periodic data output. After processing the command, the sensor performs periodic data output in the text-based form (ASCII codes) of the following data: relative level, temperature, frequency.&lt;br /&gt;
&lt;br /&gt;
The data are being output periodically with an interval set up when cofiguring the sensor (LIGO Configurator software). In case the data output interval is set to zero, the data output won&#039;t be performed.&lt;br /&gt;
&lt;br /&gt;
Switching on of the periodic data output is done by sending of the &amp;quot;DP&amp;quot; symbols in line. After processing of the command, the symbols line will be received. For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power supply.&lt;br /&gt;
&lt;br /&gt;
== Checksum Calculation Algorithm ==&lt;br /&gt;
&lt;br /&gt;
The checksum is calculated using Dallas APPLICATION NOTE 27 table method: &#039;&#039;Understanding and Using Cyclic Redundancy Checks with Dallas Semiconductor iButton Products&#039;&#039;. One can use the following algorithms to calculate the checksum with a polynom &amp;lt;code&amp;gt;x^8 + x^5 + x^4 + 1&amp;lt;/code&amp;gt; (C language):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
U8 CRC8(U8 data, U8 crc)&lt;br /&gt;
{&lt;br /&gt;
    U8 i = data ^ crc;&lt;br /&gt;
    crc = 0;&lt;br /&gt;
    if (i &amp;amp; 0x01) crc ^= 0x5e;&lt;br /&gt;
    if (i &amp;amp; 0x02) crc ^= 0xbc;&lt;br /&gt;
    if (i &amp;amp; 0x04) crc ^= 0x61;&lt;br /&gt;
    if (i &amp;amp; 0x08) crc ^= 0xc2;&lt;br /&gt;
    if (i &amp;amp; 0x10) crc ^= 0x9d;&lt;br /&gt;
    if (i &amp;amp; 0x20) crc ^= 0x23;&lt;br /&gt;
    if (i &amp;amp; 0x40) crc ^= 0x46;&lt;br /&gt;
    if (i &amp;amp; 0x80) crc ^= 0x8c;&lt;br /&gt;
    return crc;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Modbus RTU Register Mapping ==&lt;br /&gt;
The SOJI sensor is capable of communication via the RS-485 serial bus in the Modbus/RTU protocol. The sensor supports the Modbus function 03 – Read Holding Registers&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!No&lt;br /&gt;
!Address&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Description&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Unit&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0x00&lt;br /&gt;
|U16&lt;br /&gt;
|Sensor address (slave ID)&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0x01&lt;br /&gt;
|U16&lt;br /&gt;
|Min calibration (Cmin) – &#039;&#039;&#039;Low word&#039;&#039;&#039;&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0x02&lt;br /&gt;
|U16&lt;br /&gt;
|Min calibration (Cmin) – &#039;&#039;&#039;High word&#039;&#039;&#039;&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0x03&lt;br /&gt;
|U16&lt;br /&gt;
|Fuel level data (12-bit / mm / liter)&lt;br /&gt;
|raw / mm / L&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0x04&lt;br /&gt;
|U16&lt;br /&gt;
|Temperature&lt;br /&gt;
|°C&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0x05&lt;br /&gt;
|U16&lt;br /&gt;
|Reserved&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0x06&lt;br /&gt;
|U16&lt;br /&gt;
|Reserved&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0x07&lt;br /&gt;
|U16&lt;br /&gt;
|Max calibration (Cmax) – &#039;&#039;&#039;Low word&#039;&#039;&#039;&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0x08&lt;br /&gt;
|U16&lt;br /&gt;
|Max calibration (Cmax) – &#039;&#039;&#039;High word&#039;&#039;&#039;&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0x09&lt;br /&gt;
|U16&lt;br /&gt;
|Sensor height (actual height, set via app)&lt;br /&gt;
|mm&lt;br /&gt;
|-&lt;br /&gt;
|11&lt;br /&gt;
|0x0A&lt;br /&gt;
|U16&lt;br /&gt;
|OSC current&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|12&lt;br /&gt;
|0x0B&lt;br /&gt;
|U16&lt;br /&gt;
|OSC current&lt;br /&gt;
|–&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;&#039;Detailed register description&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x00 — Sensor address&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The slave address of the sensor. The master uses this value as the address byte in the request frame.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x01 + 0x02 — Min calibration (Cmin) (32-bit)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;minimum&#039;&#039;&#039; calibration value is a 32-bit value split across two 16-bit registers:&lt;br /&gt;
&lt;br /&gt;
* 0x01 = &#039;&#039;&#039;Low word&#039;&#039;&#039; (lower 16 bits)&lt;br /&gt;
* 0x02 = &#039;&#039;&#039;High word&#039;&#039;&#039; (upper 16 bits)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Combination formula:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Cmin = (RegHigh &amp;lt;&amp;lt; 16) | RegLow&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x07 + 0x08 — Max calibration (Cmax) (32-bit)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same scheme as min, this is the &#039;&#039;&#039;maximum&#039;&#039;&#039; calibration value:&lt;br /&gt;
&lt;br /&gt;
* 0x07 = Low word&lt;br /&gt;
* 0x08 = High word&lt;br /&gt;
&lt;br /&gt;
Together, the min/max pairs define the &#039;&#039;&#039;calibration range&#039;&#039;&#039; of the capacitive sensor:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Empty&#039;&#039;&#039; tank ↔ Cmin&lt;br /&gt;
* &#039;&#039;&#039;Full&#039;&#039;&#039; tank ↔ Cmax&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x03 — Fuel level data&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This register has &#039;&#039;&#039;3 output modes&#039;&#039;&#039;, selectable by the user through the &#039;&#039;&#039;mobile configuration app:&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Mode&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Value range&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Meaning&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Conversion&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Raw (12-bit)&#039;&#039;&#039;&lt;br /&gt;
|0 – 4095&lt;br /&gt;
|Raw value&lt;br /&gt;
|0 = empty, 4095 = full (per min/max calibration)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;mm (height)&#039;&#039;&#039;&lt;br /&gt;
|0 – (Sensor_height × 10)&lt;br /&gt;
|Fuel level height by actual height&lt;br /&gt;
|Actual level (mm) = Value / 10&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Liter (volume)&#039;&#039;&#039;&lt;br /&gt;
|0 – Vmax&lt;br /&gt;
|Fuel volume&lt;br /&gt;
|Per the calibration table/factor configured in the app&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;&#039;Raw mode example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reg = 2048  →  2048 / 4095 ≈ 50% of full scale&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mm mode example&#039;&#039;&#039; (output = Fuel level height × 10):&lt;br /&gt;
&lt;br /&gt;
Reg = 3500  →  Fuel level height = 3500 / 10 = 350.0 mm&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Liter mode example:&#039;&#039;&#039; depends on tank geometry + the calibration chart the user enters in the app, e.g. Reg = 80 → 80 liters.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x04 — Temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature measured at the sensor (°C).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039; Reg = 25 → 25 °C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹 0x09 — Sensor height (actual probe height)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The physical height of the sensor probe, &#039;&#039;&#039;set by the user via the app&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reg  = 5000  → Sensor height = 5000/10 = 500 mm.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x0A + 0x0B — OSC current&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Oscillator current/parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x05, 0x06 — Reserved&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reserved for future expansion, currently unused.&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO BLE ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Protocol&amp;diff=2076</id>
		<title>LIGO PRO BLE Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Protocol&amp;diff=2076"/>
		<updated>2026-06-30T11:53:15Z</updated>

		<summary type="html">&lt;p&gt;Admin: Update Modbus RTU&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LLS Protocol is used in LIGO RS232 / RS485 fuel level sensor with the following parameters:&lt;br /&gt;
&lt;br /&gt;
* Baud rate: 2400,4800,9600,19200,38400,115200 (can be configured by software on PC)&lt;br /&gt;
* Data bits: 8&lt;br /&gt;
* Parity: None&lt;br /&gt;
* Stop bits: 1&lt;br /&gt;
* Flow control: None&lt;br /&gt;
&lt;br /&gt;
LIGO fuel level sensor has 2 working modes:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Slave mode:&#039;&#039;&#039; In this mode, the sensor will respond to all requests from the external device (master device). Each sensor in the network will be distinguished by different addresses configured with the LIGO Configurator software&lt;br /&gt;
# &#039;&#039;&#039;Master mode:&#039;&#039;&#039; In this mode the sensor will automatically send data to the external device with a preset interval (message interval)&lt;br /&gt;
&lt;br /&gt;
To enable this mode on the LIGO Configurator software, do the following:&lt;br /&gt;
&lt;br /&gt;
* Automatic transmission: select HEX (binary) or ASCII or ASCII EXT&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Automatic-Transmission.png|frameless|219px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Automatic transmission setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Set the time of each transfer (Message interval)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Message-Interval.png|frameless|228px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Message interval setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Exchange Protocol Description ==&lt;br /&gt;
&lt;br /&gt;
The LLS protocol supports two types of exchange protocols: binary (HEX) and character view (transmission of ASCII sequences). It is recommended to use binary exchange protocol.&lt;br /&gt;
&lt;br /&gt;
=== Format of Binary Protocol Messages ===&lt;br /&gt;
&lt;br /&gt;
All the commands of the binary communication protocol have the same standardized format which is given in the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequential number of the field !! Field name !! Field size, byte !! Description&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 1 || Prefix || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is a marker of the message beginning shall have prefix 31h, and an outcoming messages shall be displayed with 3Eh prefix by the program.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 2 || Network address || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the network address of the recipient. &amp;lt;br&amp;gt;For prefix 3Eh specify the network address of the sender.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 3 || Operation code || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the code of operation which the program shall perform For Eh prefix specify the code of operation to which the response is given&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 4 || Data || style=&amp;quot;text-align:center;&amp;quot; | It depends on the operation code || Data composition and format of the field depends on the operation code&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 5 || Checksum || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is used to control over data integrity&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Single-Stage Data Reading (command 06h) ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The data are transmitted with a lower byte ahead.&lt;br /&gt;
&lt;br /&gt;
==== Command format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output (command 07h) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to switch on periodic data output.&lt;br /&gt;
&lt;br /&gt;
After the command is processed, the sensor starts sending data periodically — level, temperature, and frequency — with the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power power supply (if the data output mode is not istalled by default).&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Periodic data output format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output Interval Adjustment (13h command) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to set up interval of periodic data output.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 0…255 || style=&amp;quot;text-align:left;&amp;quot; | Interval of the data output in seconds&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Default Data Output Mode (command 17h) ===&lt;br /&gt;
&lt;br /&gt;
This command determines the order of data output after the sensor is powered on or the processor is reset. After the power is on or the processor is reset, the program will send data periodically via the interface at the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | +3 || rowspan=&amp;quot;4&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in binary form&lt;br /&gt;
|-&lt;br /&gt;
| 02h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII mode)&lt;br /&gt;
|-&lt;br /&gt;
| 03h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII EXT mode)&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h…FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Description of Commands for the Text-Based Protocol ==&lt;br /&gt;
&lt;br /&gt;
Data exchange via the text-based protocol includes receipt and sending of ASCII symbols sequence interpreted and the request and response commands.&lt;br /&gt;
&lt;br /&gt;
=== Reading the Data ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The command is a sequence of symbols ASCII &amp;quot;D&amp;quot; and &amp;quot;O&amp;quot;. After receipt of the &amp;quot;DO&amp;quot; command the program will response in the form of ASCII symbols sequence.&lt;br /&gt;
&lt;br /&gt;
For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. All values are in hexadecimal form.&lt;br /&gt;
&lt;br /&gt;
In case the frequency value exceeds FFFh, the data are considered invalid.&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output ===&lt;br /&gt;
&lt;br /&gt;
The command is designed to switch on periodic data output. After processing the command, the sensor performs periodic data output in the text-based form (ASCII codes) of the following data: relative level, temperature, frequency.&lt;br /&gt;
&lt;br /&gt;
The data are being output periodically with an interval set up when cofiguring the sensor (LIGO Configurator software). In case the data output interval is set to zero, the data output won&#039;t be performed.&lt;br /&gt;
&lt;br /&gt;
Switching on of the periodic data output is done by sending of the &amp;quot;DP&amp;quot; symbols in line. After processing of the command, the symbols line will be received. For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power supply.&lt;br /&gt;
&lt;br /&gt;
== Checksum Calculation Algorithm ==&lt;br /&gt;
&lt;br /&gt;
The checksum is calculated using Dallas APPLICATION NOTE 27 table method: &#039;&#039;Understanding and Using Cyclic Redundancy Checks with Dallas Semiconductor iButton Products&#039;&#039;. One can use the following algorithms to calculate the checksum with a polynom &amp;lt;code&amp;gt;x^8 + x^5 + x^4 + 1&amp;lt;/code&amp;gt; (C language):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
U8 CRC8(U8 data, U8 crc)&lt;br /&gt;
{&lt;br /&gt;
    U8 i = data ^ crc;&lt;br /&gt;
    crc = 0;&lt;br /&gt;
    if (i &amp;amp; 0x01) crc ^= 0x5e;&lt;br /&gt;
    if (i &amp;amp; 0x02) crc ^= 0xbc;&lt;br /&gt;
    if (i &amp;amp; 0x04) crc ^= 0x61;&lt;br /&gt;
    if (i &amp;amp; 0x08) crc ^= 0xc2;&lt;br /&gt;
    if (i &amp;amp; 0x10) crc ^= 0x9d;&lt;br /&gt;
    if (i &amp;amp; 0x20) crc ^= 0x23;&lt;br /&gt;
    if (i &amp;amp; 0x40) crc ^= 0x46;&lt;br /&gt;
    if (i &amp;amp; 0x80) crc ^= 0x8c;&lt;br /&gt;
    return crc;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Modbus RTU Register Mapping ==&lt;br /&gt;
The SOJI sensor is capable of communication via the RS-485 serial bus in the Modbus/RTU protocol. The sensor supports the Modbus function 03 – Read Holding Registers&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!No&lt;br /&gt;
!Address&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Description&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Unit&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|1&lt;br /&gt;
|0x00&lt;br /&gt;
|U16&lt;br /&gt;
|Sensor address (slave ID)&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|0x01&lt;br /&gt;
|U16&lt;br /&gt;
|Min calibration (Cmin) – &#039;&#039;&#039;Low word&#039;&#039;&#039;&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|0x02&lt;br /&gt;
|U16&lt;br /&gt;
|Min calibration (Cmin) – &#039;&#039;&#039;High word&#039;&#039;&#039;&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|0x03&lt;br /&gt;
|U16&lt;br /&gt;
|Fuel level data (12-bit / mm / liter)&lt;br /&gt;
|raw / mm / L&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0x04&lt;br /&gt;
|U16&lt;br /&gt;
|Temperature&lt;br /&gt;
|°C&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0x05&lt;br /&gt;
|U16&lt;br /&gt;
|Reserved&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0x06&lt;br /&gt;
|U16&lt;br /&gt;
|Reserved&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|8&lt;br /&gt;
|0x07&lt;br /&gt;
|U16&lt;br /&gt;
|Max calibration (Cmax) – &#039;&#039;&#039;Low word&#039;&#039;&#039;&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0x08&lt;br /&gt;
|U16&lt;br /&gt;
|Max calibration (Cmax) – &#039;&#039;&#039;High word&#039;&#039;&#039;&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0x09&lt;br /&gt;
|U16&lt;br /&gt;
|Sensor height (actual height, set via app)&lt;br /&gt;
|mm&lt;br /&gt;
|-&lt;br /&gt;
|11&lt;br /&gt;
|0x0A&lt;br /&gt;
|U16&lt;br /&gt;
|OSC current&lt;br /&gt;
|–&lt;br /&gt;
|-&lt;br /&gt;
|12&lt;br /&gt;
|0x0B&lt;br /&gt;
|U16&lt;br /&gt;
|OSC current&lt;br /&gt;
|–&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;&#039;Detailed register description&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x00 — Sensor address&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The slave address of the sensor. The master uses this value as the address byte in the request frame.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x01 + 0x02 — Min calibration (Cmin) (32-bit)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;minimum&#039;&#039;&#039; calibration value is a 32-bit value split across two 16-bit registers:&lt;br /&gt;
&lt;br /&gt;
* 0x01 = &#039;&#039;&#039;Low word&#039;&#039;&#039; (lower 16 bits)&lt;br /&gt;
* 0x02 = &#039;&#039;&#039;High word&#039;&#039;&#039; (upper 16 bits)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Combination formula:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Cmin = (RegHigh &amp;lt;&amp;lt; 16) | RegLow&lt;br /&gt;
&lt;br /&gt;
            = RegHigh × 65536 + RegLow&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x07 + 0x08 — Max calibration (Cmax) (32-bit)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same scheme as min, this is the &#039;&#039;&#039;maximum&#039;&#039;&#039; calibration value:&lt;br /&gt;
&lt;br /&gt;
* 0x07 = Low word&lt;br /&gt;
* 0x08 = High word&lt;br /&gt;
&lt;br /&gt;
Together, the min/max pairs define the &#039;&#039;&#039;calibration range&#039;&#039;&#039; of the capacitive sensor:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Empty&#039;&#039;&#039; tank ↔ Cmin&lt;br /&gt;
* &#039;&#039;&#039;Full&#039;&#039;&#039; tank ↔ Cmax&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x03 — Fuel level data&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This register has &#039;&#039;&#039;3 output modes&#039;&#039;&#039;, selectable by the user through the &#039;&#039;&#039;mobile configuration app:&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Mode&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Value range&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Meaning&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Conversion&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Raw (12-bit)&#039;&#039;&#039;&lt;br /&gt;
|0 – 4095&lt;br /&gt;
|Raw value&lt;br /&gt;
|0 = empty, 4095 = full (per min/max calibration)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;mm (height)&#039;&#039;&#039;&lt;br /&gt;
|0 – (Sensor_height × 10)&lt;br /&gt;
|Fuel level height by actual height&lt;br /&gt;
|Actual level (mm) = Value / 10&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;3. Liter (volume)&#039;&#039;&#039;&lt;br /&gt;
|0 – Vmax&lt;br /&gt;
|Fuel volume&lt;br /&gt;
|Per the calibration table/factor configured in the app&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;&#039;Raw mode example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reg = 2048  →  2048 / 4095 ≈ 50% of full scale&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mm mode example&#039;&#039;&#039; (output = Fuel level height × 10):&lt;br /&gt;
&lt;br /&gt;
Reg = 3500  →  Fuel level height = 3500 / 10 = 350.0 mm&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Liter mode example:&#039;&#039;&#039; depends on tank geometry + the calibration chart the user enters in the app, e.g. Reg = 80 → 80 liters.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x04 — Temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature measured at the sensor (°C).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039; Reg = 25 → 25 °C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹 0x09 — Sensor height (actual probe height)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The physical height of the sensor probe, &#039;&#039;&#039;set by the user via the app&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reg  = 5000  → Sensor height = 5000/10 = 500 mm.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x0A + 0x0B — OSC current&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Oscillator current/parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x05, 0x06 — Reserved&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reserved for future expansion, currently unused.&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO BLE ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Protocol&amp;diff=2075</id>
		<title>LIGO PRO 2 Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Protocol&amp;diff=2075"/>
		<updated>2026-06-30T11:51:23Z</updated>

		<summary type="html">&lt;p&gt;Admin: Update Modbus RTU&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LLS Protocol is used in LIGO RS232 / RS485 fuel level sensor with the following parameters:&lt;br /&gt;
&lt;br /&gt;
* Baud rate: 2400,4800,9600,19200,38400,115200 (can be configured by software on PC or via LIGO BLE Configurator application on mobile phone)&lt;br /&gt;
* Data bits: 8&lt;br /&gt;
* Parity: None&lt;br /&gt;
* Stop bits: 1&lt;br /&gt;
* Flow control: None&lt;br /&gt;
&lt;br /&gt;
LIGO fuel level sensor has 2 working modes:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Slave mode:&#039;&#039;&#039; In this mode, the sensor will respond to all requests from the external device (master device). Each sensor in the network will be distinguished by different addresses configured with the LIGO Configurator software&lt;br /&gt;
# &#039;&#039;&#039;Master mode:&#039;&#039;&#039; In this mode the sensor will automatically send data to the external device with a preset interval (message interval)&lt;br /&gt;
&lt;br /&gt;
To enable this mode on the LIGO Configurator software, do the following:&lt;br /&gt;
&lt;br /&gt;
* Automatic transmission: select HEX (binary) or ASCII or ASCII EXT&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Automatic-Transmission.png|frameless|219px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Automatic transmission setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Set the time of each transfer (Message interval)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Message-Interval.png|frameless|228px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Message interval setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Exchange Protocol Description ==&lt;br /&gt;
&lt;br /&gt;
The LLS protocol supports two types of exchange protocols: binary (HEX) and character view (transmission of ASCII sequences). It is recommended to use binary exchange protocol.&lt;br /&gt;
&lt;br /&gt;
=== Format of Binary Protocol Messages ===&lt;br /&gt;
&lt;br /&gt;
All the commands of the binary communication protocol have the same standardized format which is given in the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequential number of the field !! Field name !! Field size, byte !! Description&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 1 || Prefix || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is a marker of the message beginning shall have prefix 31h, and an outcoming messages shall be displayed with 3Eh prefix by the program.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 2 || Network address || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the network address of the recipient. &amp;lt;br&amp;gt;For prefix 3Eh specify the network address of the sender.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 3 || Operation code || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the code of operation which the program shall perform For Eh prefix specify the code of operation to which the response is given&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 4 || Data || style=&amp;quot;text-align:center;&amp;quot; | It depends on the operation code || Data composition and format of the field depends on the operation code&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 5 || Checksum || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is used to control over data integrity&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Single-Stage Data Reading (command 06h) ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The data are transmitted with a lower byte ahead.&lt;br /&gt;
&lt;br /&gt;
==== Command format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output (command 07h) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to switch on periodic data output.&lt;br /&gt;
&lt;br /&gt;
After the command is processed, the sensor starts sending data periodically — level, temperature, and frequency — with the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power power supply (if the data output mode is not istalled by default).&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Periodic data output format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output Interval Adjustment (13h command) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to set up interval of periodic data output.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 0…255 || style=&amp;quot;text-align:left;&amp;quot; | Interval of the data output in seconds&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Default Data Output Mode (command 17h) ===&lt;br /&gt;
&lt;br /&gt;
This command determines the order of data output after the sensor is powered on or the processor is reset. After the power is on or the processor is reset, the program will send data periodically via the interface at the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | +3 || rowspan=&amp;quot;4&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in binary form&lt;br /&gt;
|-&lt;br /&gt;
| 02h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII mode)&lt;br /&gt;
|-&lt;br /&gt;
| 03h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII EXT mode)&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h…FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Description of Commands for the Text-Based Protocol ==&lt;br /&gt;
&lt;br /&gt;
Data exchange via the text-based protocol includes receipt and sending of ASCII symbols sequence interpreted and the request and response commands.&lt;br /&gt;
&lt;br /&gt;
=== Reading the Data ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The command is a sequence of symbols ASCII &amp;quot;D&amp;quot; and &amp;quot;O&amp;quot;. After receipt of the &amp;quot;DO&amp;quot; command the program will response in the form of ASCII symbols sequence.&lt;br /&gt;
&lt;br /&gt;
For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. All values are in hexadecimal form.&lt;br /&gt;
&lt;br /&gt;
In case the frequency value exceeds FFFh, the data are considered invalid.&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output ===&lt;br /&gt;
&lt;br /&gt;
The command is designed to switch on periodic data output. After processing the command, the sensor performs periodic data output in the text-based form (ASCII codes) of the following data: relative level, temperature, frequency.&lt;br /&gt;
&lt;br /&gt;
The data are being output periodically with an interval set up when cofiguring the sensor (LIGO Configurator software). In case the data output interval is set to zero, the data output won&#039;t be performed.&lt;br /&gt;
&lt;br /&gt;
Switching on of the periodic data output is done by sending of the &amp;quot;DP&amp;quot; symbols in line. After processing of the command, the symbols line will be received. For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power supply.&lt;br /&gt;
&lt;br /&gt;
== Checksum Calculation Algorithm ==&lt;br /&gt;
&lt;br /&gt;
The checksum is calculated using Dallas APPLICATION NOTE 27 table method: &#039;&#039;Understanding and Using Cyclic Redundancy Checks with Dallas Semiconductor iButton Products&#039;&#039;. One can use the following algorithms to calculate the checksum with a polynom &amp;lt;code&amp;gt;x^8 + x^5 + x^4 + 1&amp;lt;/code&amp;gt; (C language):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
U8 CRC8(U8 data, U8 crc)&lt;br /&gt;
{&lt;br /&gt;
    U8 i = data ^ crc;&lt;br /&gt;
    crc = 0;&lt;br /&gt;
    if (i &amp;amp; 0x01) crc ^= 0x5e;&lt;br /&gt;
    if (i &amp;amp; 0x02) crc ^= 0xbc;&lt;br /&gt;
    if (i &amp;amp; 0x04) crc ^= 0x61;&lt;br /&gt;
    if (i &amp;amp; 0x08) crc ^= 0xc2;&lt;br /&gt;
    if (i &amp;amp; 0x10) crc ^= 0x9d;&lt;br /&gt;
    if (i &amp;amp; 0x20) crc ^= 0x23;&lt;br /&gt;
    if (i &amp;amp; 0x40) crc ^= 0x46;&lt;br /&gt;
    if (i &amp;amp; 0x80) crc ^= 0x8c;&lt;br /&gt;
    return crc;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Modbus RTU Register Mapping ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The SOJI sensor is capable of communication via the RS-485 serial bus in the Modbus/RTU protocol. The sensor supports the Modbus function 03 – Read Holding Registers&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! No !! Address &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Description&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Unit&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 1 || 0x00 &lt;br /&gt;
|U16&lt;br /&gt;
|Sensor address (slave ID) &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
| 2 || 0x01 &lt;br /&gt;
|U16&lt;br /&gt;
|Min calibration (Cmin) – &#039;&#039;&#039;Low word&#039;&#039;&#039; &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
| 3 ||0x02 &lt;br /&gt;
|U16&lt;br /&gt;
|Min calibration (Cmin) – &#039;&#039;&#039;High word&#039;&#039;&#039; &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
| 4 &lt;br /&gt;
|0x03&lt;br /&gt;
|U16&lt;br /&gt;
|Fuel level data (12-bit / mm / liter) &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |raw / mm / L&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|0x04&lt;br /&gt;
|U16&lt;br /&gt;
|Temperature &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |°C&lt;br /&gt;
|-&lt;br /&gt;
|6&lt;br /&gt;
|0x05&lt;br /&gt;
|U16&lt;br /&gt;
|Reserved &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
|7&lt;br /&gt;
|0x06&lt;br /&gt;
|U16&lt;br /&gt;
|Reserved &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
| 8 ||0x07 &lt;br /&gt;
|U16&lt;br /&gt;
|Max calibration (Cmax) – &#039;&#039;&#039;Low word&#039;&#039;&#039; &lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|0x08&lt;br /&gt;
|U16&lt;br /&gt;
|Max calibration (Cmax) – &#039;&#039;&#039;High word&#039;&#039;&#039;&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
|10&lt;br /&gt;
|0x09&lt;br /&gt;
|U16&lt;br /&gt;
|Sensor height (actual height, set via app)&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot; |mm&lt;br /&gt;
|-&lt;br /&gt;
|11&lt;br /&gt;
|0x0A&lt;br /&gt;
|U16&lt;br /&gt;
|OSC current&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot; |–&lt;br /&gt;
|-&lt;br /&gt;
|12&lt;br /&gt;
|0x0B&lt;br /&gt;
|U16&lt;br /&gt;
|OSC current&lt;br /&gt;
| style=&amp;quot;text-align: left;&amp;quot; |–&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;&#039;Detailed register description&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x00 — Sensor address&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The slave address of the sensor. The master uses this value as the address byte in the request frame.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x01 + 0x02 — Min calibration (Cmin) (32-bit)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;&#039;minimum&#039;&#039;&#039; calibration value is a 32-bit value split across two 16-bit registers:&lt;br /&gt;
&lt;br /&gt;
* 0x01 = &#039;&#039;&#039;Low     word&#039;&#039;&#039; (lower 16 bits)&lt;br /&gt;
* 0x02 = &#039;&#039;&#039;High     word&#039;&#039;&#039; (upper 16 bits)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Combination formula:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Cmin = (RegHigh &amp;lt;&amp;lt; 16) | RegLow&lt;br /&gt;
&lt;br /&gt;
            = RegHigh × 65536 + RegLow&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x07 + 0x08 — Max calibration (Cmax) (32-bit)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same scheme as min, this is the &#039;&#039;&#039;maximum&#039;&#039;&#039; calibration value:&lt;br /&gt;
&lt;br /&gt;
* 0x07 =  Low word&lt;br /&gt;
* 0x08 =  High word&lt;br /&gt;
&lt;br /&gt;
Together, the min/max pairs define the &#039;&#039;&#039;calibration range&#039;&#039;&#039; of the capacitive sensor:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Empty&#039;&#039;&#039; tank ↔ Cmin&lt;br /&gt;
* &#039;&#039;&#039;Full&#039;&#039;&#039; tank ↔ Cmax&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x03 — Fuel level data&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
This register has &#039;&#039;&#039;3 output modes&#039;&#039;&#039;, selectable by the user through the &#039;&#039;&#039;mobile configuration app:&#039;&#039;&#039;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Mode&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Value range&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Meaning&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Conversion&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Raw (12-bit)&#039;&#039;&#039;&lt;br /&gt;
|0 – 4095&lt;br /&gt;
|Raw value&lt;br /&gt;
|0 = empty, 4095 = full (per min/max calibration)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;mm (height)&#039;&#039;&#039;&lt;br /&gt;
|0 – (Sensor_height × 10)&lt;br /&gt;
|Fuel level height by actual height&lt;br /&gt;
|Actual level (mm) = Value / 10&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;3. Liter (volume)&#039;&#039;&#039;&lt;br /&gt;
|0 – Vmax&lt;br /&gt;
|Fuel volume&lt;br /&gt;
|Per the calibration table/factor configured in the app&lt;br /&gt;
|}&lt;br /&gt;
&#039;&#039;&#039;Raw mode example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reg = 2048  →  2048 / 4095 ≈ 50% of full scale&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;mm mode example&#039;&#039;&#039; (output = Fuel level height × 10):&lt;br /&gt;
&lt;br /&gt;
Reg = 3500  →  Fuel level height = 3500 / 10 = 350.0 mm&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Liter mode example:&#039;&#039;&#039; depends on tank geometry + the calibration chart the user enters in the app, e.g. Reg = 80 → 80 liters.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x04 — Temperature&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Temperature measured at the sensor (°C).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039; Reg = 25 → 25 °C.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹 0x09 — Sensor height (actual probe height)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The physical height of the sensor probe, &#039;&#039;&#039;set by the user via the app&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reg  = 5000  → Sensor height = 5000/10 = 500 mm&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x0A + 0x0B — OSC current&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Oscillator current/parameter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;🔹&#039;&#039;&#039; &#039;&#039;&#039;0x05, 0x06 — Reserved&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Reserved for future expansion, currently unused.&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2074</id>
		<title>LIGO AIR Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2074"/>
		<updated>2026-06-29T01:56:14Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Advertising Message Format */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LIGO AIR Protocol utilizes &#039;&#039;&#039;Bluetooth Low Energy (BLE)&#039;&#039;&#039; to transmit structured fuel sensor data in advertising packets at regular intervals. Data includes fuel level, battery voltage of sensor, temperature, etc. For long-range applications, &#039;&#039;&#039;LIGO AIR Adapter&#039;&#039;&#039; is recommended to boost reception.&lt;br /&gt;
&lt;br /&gt;
In addition to its native protocol, LIGO AIR supports both the &#039;&#039;&#039;Escort&#039;&#039;&#039; and &#039;&#039;&#039;Mielta&#039;&#039;&#039; protocols, enabling seamless protocol switching without requiring additional integration effort. Users can configure the desired protocol via the mobile application.&lt;br /&gt;
&lt;br /&gt;
== Advertising Message Format ==&lt;br /&gt;
&lt;br /&gt;
Structure of &#039;&#039;&#039;Manufacturer Data&#039;&#039;&#039; field in advertising message (same for any type of peripheral unit).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example Manufacturer Data:&#039;&#039;&#039; &amp;lt;code&amp;gt;0x0CFFFC0D024D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%; text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequence number of bytes !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Length of field || &amp;lt;code&amp;gt;0x0C&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +1 || Type of field (Manufacturer data) || Always &amp;lt;code&amp;gt;0xFF&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +2 || Company ID || &amp;lt;code&amp;gt;0x0DFC&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +4 || Hardware ID (type of device) || &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; — LIGO Air |&lt;br /&gt;
|-&lt;br /&gt;
| +5 and further || Payload (sensor parameters) || &amp;lt;code&amp;gt;0x4D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In order to distinguish between types of hardware, the &#039;&#039;&#039;Hardware ID&#039;&#039;&#039; byte is used (see list of hardware below). The Hardware ID byte defines a set of useful parameters from the peripheral unit, their size, and order.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Byte of Hardware ID !! Type of Hardware&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || style=&amp;quot;text-align:left; | LIGO Air&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || style=&amp;quot;text-align:left; | LIGO Air Pro&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;The list of hardware may be expanded.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Payload data format of LIGO AIR (Hardware ID &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt;) and LIGO AIR Pro (Hardware ID &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt;).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
LIGO Air and LIGO Air Pro have the same payload data format. The structure of the payload is listed in the table below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%; text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequence number of bytes !! Parameter type !! Parameter length (bytes) !! Range !! Unit of measurement&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Fuel level || 2 || 0 to 1023 (option), &amp;lt;br&amp;gt;0 to 4095 (default), &amp;lt;br&amp;gt;or 0 to 65535 (option, liters) || Arbitrary units&lt;br /&gt;
|-&lt;br /&gt;
| +2 || Reserved || 1 || N/A || N/A&lt;br /&gt;
|-&lt;br /&gt;
| +3 || Built-in battery voltage || 1 || 0 to 255 || Volt × 10&lt;br /&gt;
|-&lt;br /&gt;
| +4 || Temperature || 1 || −128 to 127 || Celsius degrees&lt;br /&gt;
|-&lt;br /&gt;
| +5 || Reserved || 1 || N/A || N/A&lt;br /&gt;
|-&lt;br /&gt;
| +6 || Firmware version || 1 || 0 to 255 || Version number&lt;br /&gt;
|-&lt;br /&gt;
| +7 || Operation Mode || 1 || 0 and 1 || 0: Working Mode &amp;lt;br&amp;gt;1: Storage Mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example Payload data:&#039;&#039;&#039; &amp;lt;code&amp;gt;0x4D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
{| style=&amp;quot;border-collapse:collapse;margin:0 auto;text-align:center;font-family:Arial,sans-serif;font-size:11pt;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
| colspan=&amp;quot;7&amp;quot; style=&amp;quot;border:none;background:transparent;padding:0 0 6px 0;font-size:50pt;font-family:Consolas,&#039;Courier New&#039;,monospace;font-weight:bold;letter-spacing:1px;text-align:center;&amp;quot; | &amp;lt;span style=&amp;quot;color:#222&amp;quot;&amp;gt;0x&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#5E7440&amp;quot;&amp;gt;4D01&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#C99D3F&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#3170A8&amp;quot;&amp;gt;24&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#1F2D52&amp;quot;&amp;gt;1F&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#6E408A&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#1A1A1A&amp;quot;&amp;gt;06&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#7BC571&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
! style=&amp;quot;background:#5E7440;color:#fff;padding:10px 18px;font-weight:bold;min-width:80px;&amp;quot; | 4D01&lt;br /&gt;
! style=&amp;quot;background:#C99D3F;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | NA&lt;br /&gt;
! style=&amp;quot;background:#3170A8;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 24&lt;br /&gt;
! style=&amp;quot;background:#1F2D52;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 1F&lt;br /&gt;
! style=&amp;quot;background:#6E408A;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | NA&lt;br /&gt;
! style=&amp;quot;background:#1A1A1A;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 06&lt;br /&gt;
! style=&amp;quot;background:#7BC571;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 00&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Fuel level&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Reserved&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Battery voltage&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Temperature&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Reserved&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Firmware version&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Operation Mode&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to little-endian (hex)&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 014D&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 24&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 1F&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 06&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 00&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to decimal&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 333&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 36&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 31&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 006&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to value&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 333 (unit or liter)&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 3.6 Volt&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 31 ºC&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 0.0.6&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | Working mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2073</id>
		<title>LIGO AIR Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2073"/>
		<updated>2026-06-29T01:55:42Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Advertising Message Format */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LIGO AIR Protocol utilizes &#039;&#039;&#039;Bluetooth Low Energy (BLE)&#039;&#039;&#039; to transmit structured fuel sensor data in advertising packets at regular intervals. Data includes fuel level, battery voltage of sensor, temperature, etc. For long-range applications, &#039;&#039;&#039;LIGO AIR Adapter&#039;&#039;&#039; is recommended to boost reception.&lt;br /&gt;
&lt;br /&gt;
In addition to its native protocol, LIGO AIR supports both the &#039;&#039;&#039;Escort&#039;&#039;&#039; and &#039;&#039;&#039;Mielta&#039;&#039;&#039; protocols, enabling seamless protocol switching without requiring additional integration effort. Users can configure the desired protocol via the mobile application.&lt;br /&gt;
&lt;br /&gt;
== Advertising Message Format ==&lt;br /&gt;
&lt;br /&gt;
Structure of &#039;&#039;&#039;Manufacturer Data&#039;&#039;&#039; field in advertising message (same for any type of peripheral unit).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example Manufacturer Data:&#039;&#039;&#039; &amp;lt;code&amp;gt;0x0CFFFC0D024D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%; text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequence number of bytes !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Length of field || &amp;lt;code&amp;gt;0x0C&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +1 || Type of field (Manufacturer data) || Always &amp;lt;code&amp;gt;0xFF&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +2 || Company ID || &amp;lt;code&amp;gt;0x0DFC&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +4 || Hardware ID (type of device) || &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; — LIGO Air |&lt;br /&gt;
|-&lt;br /&gt;
| +5 and further || Payload (sensor parameters) || &amp;lt;code&amp;gt;0x4D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In order to distinguish between types of hardware, the &#039;&#039;&#039;Hardware ID&#039;&#039;&#039; byte is used (see list of hardware below). The Hardware ID byte defines a set of useful parameters from the peripheral unit, their size, and order.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Byte of Hardware ID !! Type of Hardware&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || style=&amp;quot;text-align:left; | LIGO Air&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || style=&amp;quot;text-align:left; | LIGO Air Pro&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;The list of hardware may be expanded.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Payload data format of LIGO AIR (Hardware ID &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt;) and LIGO AIR Pro (Hardware ID &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt;).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
LIGO Air and LIGO Air Pro have the same payload data format. The structure of the payload is listed in the table below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequence number of bytes !! Parameter type !! Parameter length (bytes) !! Range !! Unit of measurement&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Fuel level || 2 || 0 to 1023 (option), &amp;lt;br&amp;gt;0 to 4095 (default), &amp;lt;br&amp;gt;or 0 to 65535 (option, liters) || Arbitrary units&lt;br /&gt;
|-&lt;br /&gt;
| +2 || Reserved || 1 || N/A || N/A&lt;br /&gt;
|-&lt;br /&gt;
| +3 || Built-in battery voltage || 1 || 0 to 255 || Volt × 10&lt;br /&gt;
|-&lt;br /&gt;
| +4 || Temperature || 1 || −128 to 127 || Celsius degrees&lt;br /&gt;
|-&lt;br /&gt;
| +5 || Reserved || 1 || N/A || N/A&lt;br /&gt;
|-&lt;br /&gt;
| +6 || Firmware version || 1 || 0 to 255 || Version number&lt;br /&gt;
|-&lt;br /&gt;
| +7 || Operation Mode || 1 || 0 and 1 || 0: Working Mode &amp;lt;br&amp;gt;1: Storage Mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example Payload data:&#039;&#039;&#039; &amp;lt;code&amp;gt;0x4D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
{| style=&amp;quot;border-collapse:collapse;margin:0 auto;text-align:center;font-family:Arial,sans-serif;font-size:11pt;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
| colspan=&amp;quot;7&amp;quot; style=&amp;quot;border:none;background:transparent;padding:0 0 6px 0;font-size:50pt;font-family:Consolas,&#039;Courier New&#039;,monospace;font-weight:bold;letter-spacing:1px;text-align:center;&amp;quot; | &amp;lt;span style=&amp;quot;color:#222&amp;quot;&amp;gt;0x&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#5E7440&amp;quot;&amp;gt;4D01&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#C99D3F&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#3170A8&amp;quot;&amp;gt;24&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#1F2D52&amp;quot;&amp;gt;1F&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#6E408A&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#1A1A1A&amp;quot;&amp;gt;06&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#7BC571&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
! style=&amp;quot;background:#5E7440;color:#fff;padding:10px 18px;font-weight:bold;min-width:80px;&amp;quot; | 4D01&lt;br /&gt;
! style=&amp;quot;background:#C99D3F;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | NA&lt;br /&gt;
! style=&amp;quot;background:#3170A8;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 24&lt;br /&gt;
! style=&amp;quot;background:#1F2D52;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 1F&lt;br /&gt;
! style=&amp;quot;background:#6E408A;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | NA&lt;br /&gt;
! style=&amp;quot;background:#1A1A1A;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 06&lt;br /&gt;
! style=&amp;quot;background:#7BC571;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 00&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Fuel level&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Reserved&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Battery voltage&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Temperature&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Reserved&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Firmware version&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Operation Mode&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to little-endian (hex)&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 014D&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 24&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 1F&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 06&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 00&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to decimal&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 333&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 36&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 31&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 006&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to value&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 333 (unit or liter)&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 3.6 Volt&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 31 ºC&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 0.0.6&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | Working mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2072</id>
		<title>LIGO AIR Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2072"/>
		<updated>2026-06-29T01:53:53Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Advertising Message Format */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LIGO AIR Protocol utilizes &#039;&#039;&#039;Bluetooth Low Energy (BLE)&#039;&#039;&#039; to transmit structured fuel sensor data in advertising packets at regular intervals. Data includes fuel level, battery voltage of sensor, temperature, etc. For long-range applications, &#039;&#039;&#039;LIGO AIR Adapter&#039;&#039;&#039; is recommended to boost reception.&lt;br /&gt;
&lt;br /&gt;
In addition to its native protocol, LIGO AIR supports both the &#039;&#039;&#039;Escort&#039;&#039;&#039; and &#039;&#039;&#039;Mielta&#039;&#039;&#039; protocols, enabling seamless protocol switching without requiring additional integration effort. Users can configure the desired protocol via the mobile application.&lt;br /&gt;
&lt;br /&gt;
== Advertising Message Format ==&lt;br /&gt;
&lt;br /&gt;
Structure of &#039;&#039;&#039;Manufacturer Data&#039;&#039;&#039; field in advertising message (same for any type of peripheral unit).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example Manufacturer Data:&#039;&#039;&#039; &amp;lt;code&amp;gt;0x0CFFFC0D024D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequence number of bytes !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Length of field || &amp;lt;code&amp;gt;0x0C&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +1 || Type of field (Manufacturer data) || Always &amp;lt;code&amp;gt;0xFF&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +2 || Company ID || &amp;lt;code&amp;gt;0x0DFC&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +4 || Hardware ID (type of device) || &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; — LIGO Air |&lt;br /&gt;
|-&lt;br /&gt;
| +5 and further || Payload (sensor parameters) || &amp;lt;code&amp;gt;0x4D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In order to distinguish between types of hardware, the &#039;&#039;&#039;Hardware ID&#039;&#039;&#039; byte is used (see list of hardware below). The Hardware ID byte defines a set of useful parameters from the peripheral unit, their size, and order.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Byte of Hardware ID !! Type of Hardware&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || style=&amp;quot;text-align:left; | LIGO Air&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || style=&amp;quot;text-align:left; | LIGO Air Pro&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;The list of hardware may be expanded.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Payload data format of LIGO AIR (Hardware ID &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt;) and LIGO AIR Pro (Hardware ID &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt;).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
LIGO Air and LIGO Air Pro have the same payload data format. The structure of the payload is listed in the table below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequence number of bytes !! Parameter type !! Parameter length (bytes) !! Range !! Unit of measurement&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Fuel level || 2 || 0 to 1023 (option), &amp;lt;br&amp;gt;0 to 4095 (default), &amp;lt;br&amp;gt;or 0 to 65535 (option, liters) || Arbitrary units&lt;br /&gt;
|-&lt;br /&gt;
| +2 || Reserved || 1 || N/A || N/A&lt;br /&gt;
|-&lt;br /&gt;
| +3 || Built-in battery voltage || 1 || 0 to 255 || Volt × 10&lt;br /&gt;
|-&lt;br /&gt;
| +4 || Temperature || 1 || −128 to 127 || Celsius degrees&lt;br /&gt;
|-&lt;br /&gt;
| +5 || Reserved || 1 || N/A || N/A&lt;br /&gt;
|-&lt;br /&gt;
| +6 || Firmware version || 1 || 0 to 255 || Version number&lt;br /&gt;
|-&lt;br /&gt;
| +7 || Operation Mode || 1 || 0 and 1 || 0: Working Mode &amp;lt;br&amp;gt;1: Storage Mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example Payload data:&#039;&#039;&#039; &amp;lt;code&amp;gt;0x4D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
{| style=&amp;quot;border-collapse:collapse;margin:0 auto;text-align:center;font-family:Arial,sans-serif;font-size:11pt;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
| colspan=&amp;quot;7&amp;quot; style=&amp;quot;border:none;background:transparent;padding:0 0 6px 0;font-size:50pt;font-family:Consolas,&#039;Courier New&#039;,monospace;font-weight:bold;letter-spacing:1px;text-align:center;&amp;quot; | &amp;lt;span style=&amp;quot;color:#222&amp;quot;&amp;gt;0x&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#5E7440&amp;quot;&amp;gt;4D01&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#C99D3F&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#3170A8&amp;quot;&amp;gt;24&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#1F2D52&amp;quot;&amp;gt;1F&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#6E408A&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#1A1A1A&amp;quot;&amp;gt;06&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#7BC571&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
! style=&amp;quot;background:#5E7440;color:#fff;padding:10px 18px;font-weight:bold;min-width:80px;&amp;quot; | 4D01&lt;br /&gt;
! style=&amp;quot;background:#C99D3F;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | NA&lt;br /&gt;
! style=&amp;quot;background:#3170A8;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 24&lt;br /&gt;
! style=&amp;quot;background:#1F2D52;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 1F&lt;br /&gt;
! style=&amp;quot;background:#6E408A;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | NA&lt;br /&gt;
! style=&amp;quot;background:#1A1A1A;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 06&lt;br /&gt;
! style=&amp;quot;background:#7BC571;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 00&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Fuel level&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Reserved&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Battery voltage&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Temperature&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Reserved&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Firmware version&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Operation Mode&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to little-endian (hex)&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 014D&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 24&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 1F&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 06&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 00&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to decimal&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 333&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 36&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 31&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 006&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to value&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 333 (unit or liter)&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 3.6 Volt&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 31 ºC&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 0.0.6&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | Working mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Datasheet&amp;diff=2071</id>
		<title>LIGO PRO 2 Datasheet</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Datasheet&amp;diff=2071"/>
		<updated>2026-06-29T01:31:15Z</updated>

		<summary type="html">&lt;p&gt;Admin: change content&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Product Overview ==&lt;br /&gt;
&lt;br /&gt;
The Ligo PRO 2 Fuel Level Sensor is produced and developed by SOJI Electronics JSC. The device is designed to measure the level of liquid fuel and other non-conductive liquids in vehicle tanks and stationary fuel storage, applicable across different fields. The measured values are transmitted to an external device as output signals such as Analog, Frequency, RS232, or RS485.&lt;br /&gt;
&lt;br /&gt;
Ligo PRO 2 was created to satisfy the increasing demand of the transport telematics market for improved reliability of design, data accuracy, and ease of installation. The sensor is integrated with Bluetooth Low Energy (BLE) making installation more convenient, easier, and faster.&lt;br /&gt;
&lt;br /&gt;
In addition, the heavy-duty metal structure helps increase protection against electrostatic, electromagnetic and conducted interference. The Ligo PRO 2 is also designed with IP69K waterproof standards and superior shock and vibration resistance.&lt;br /&gt;
&lt;br /&gt;
== Key Features ==&lt;br /&gt;
&lt;br /&gt;
*High accuracy up to 99.5%&lt;br /&gt;
*Wide range input voltage: 6-40V&lt;br /&gt;
*Galvanic isolation up to 2500V&lt;br /&gt;
*Configurable length up to 6000mm&lt;br /&gt;
*Operating temperature: -40°C to +85°C&lt;br /&gt;
*Built-in anti-mud oil filter and anti-vibration springs&lt;br /&gt;
*IP69K waterproof standard&lt;br /&gt;
*Integrated BLE technology for smartphone configuration&lt;br /&gt;
&lt;br /&gt;
== Applications ==&lt;br /&gt;
&lt;br /&gt;
*Trucks, excavators, heavy construction machines&lt;br /&gt;
*Marine vessels and electric generators&lt;br /&gt;
*Off-highway machinery including high-temperature and vibration environments&lt;br /&gt;
*Industrial oil storage tanks or stationary storage tanks&lt;br /&gt;
&lt;br /&gt;
== Product Variants / Ordering Information ==&lt;br /&gt;
&lt;br /&gt;
== Technical Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500 ... up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0-9V) || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || colspan=&amp;quot;3&amp;quot; | 2400, 4800, 9600, 19200, 38400, 115200&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V) || colspan=&amp;quot;3&amp;quot; | 6-40V&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Supply protection (Over-voltage 100 VDC for 2 min. Reverse polarity) || colspan=&amp;quot;3&amp;quot; | Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40 ... +85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level, % || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output range (min level) || Analog (0...8V) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output range (max level) || Analog (1...9V) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 0 ... 255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, s || Continuous || 1 ... 255 || 1 ... 255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement, °C || colspan=&amp;quot;3&amp;quot; | ±1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.1, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity, +8 dBm TX Power&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || colspan=&amp;quot;3&amp;quot; | 10&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Dimensions ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig1-dimensions.jpg|frameless|915px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Ligo PRO 2 overall dimensions (cm)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wiring Connection ===&lt;br /&gt;
&lt;br /&gt;
==== AF (Analog &amp;amp; Frequency) ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig2-wiring-AF.png|frameless|892px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Wiring diagram – Analog and Frequency output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== RS232 ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig3-wiring-RS232.png|frameless|903px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Wiring diagram – RS232 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== RS485 ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig4-wiring-RS485.png|frameless|867px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Wiring diagram – RS485 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Connector ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig5-connector.png|frameless|703px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. Connector pin details&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== AF / RS232 Pinout ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Description !! Wire Color&lt;br /&gt;
|-&lt;br /&gt;
| 1 || GND || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 2 || NC (Not connected) || NC&lt;br /&gt;
|-&lt;br /&gt;
| 3 || VCC (6-40V) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || TXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Analog Output || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#fff;border:1px solid #999;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;White&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 6 || RXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#7b3f00;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Brown&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== RS485 Pinout ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Description !! Wire Color&lt;br /&gt;
|-&lt;br /&gt;
| 1 || GND || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 2 || RS485 (B) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#2e8b2e;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Green&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 3 || VCC (6-40V) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || TXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 5 || RS485 (A) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 6 || RXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#7b3f00;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Brown&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Compliance and Certifications ==&lt;br /&gt;
Refer to [[LIGO PRO 2 Certification &amp;amp; Approvals]] for current certificate status.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2 ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_User_Guide&amp;diff=2070</id>
		<title>LIGO AIR User Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_User_Guide&amp;diff=2070"/>
		<updated>2026-06-26T01:55:19Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Technical specifications */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Safety Information ==&lt;br /&gt;
{{Notice&lt;br /&gt;
| title = Important notes during sensor installation&lt;br /&gt;
| body = *Install the sensor in the direction of maximum signal transmission (the directional arrow on the sensor cover should point toward the receiving device).&lt;br /&gt;
*Minimum allowable length of the measuring probe is 150 mm.&lt;br /&gt;
*Make sure there is no metal plate covering the sensor head, as this may block Bluetooth signal.&lt;br /&gt;
*Ensure that the signal strength (RSSI) at the receiving device is not less than -80 dBm. In cases of weak signal, it is necessary to use a LIGO PRO or a LIGO AIR Adapter to ensure the transmission strength remains within the acceptable range.&lt;br /&gt;
*Change the default password provided by the manufacturer to prevent unauthorized access.&lt;br /&gt;
*Detailed instructions for using mobile application (LIGO BLE configurator) are described in detail in the ‘LIGO BLE Configurator App User Guide’ document.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Product Overview ==&lt;br /&gt;
&lt;br /&gt;
This user guide provides detailed instructions for the proper installation of the LIGO AIR fuel level sensor. The guide is intended for technicians, engineers, system integrators and end-users who are responsible for installing and maintaining the sensor in a variety of fuel tanks and vehicles.&lt;br /&gt;
&lt;br /&gt;
The LIGO AIR fuel level sensor is a precision device designed by SOJI Electronics to measure the level of fuel in tanks with high accuracy and reliability. Correct installation is critical to ensure optimal performance and long-term durability.&lt;br /&gt;
&lt;br /&gt;
Please read this guide thoroughly before beginning the installation process. Following the steps and recommendations provided will help you avoid common issues and ensure safe, effective operation of the sensor.&lt;br /&gt;
&lt;br /&gt;
== Package Contents ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! No. !! Description !! Qty (pcs)&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR Fuel Level Sensor. Standard lengths: 700, 1000, and 1500mm (for customized lengths, please contact the manufacturer) || 01&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | Oil filter || 01&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Gasoline-resistant rubber gasket || 01&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Bottom-stop and springs || 01&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | Self-drilling screw M4.8x32mm || 05&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Rivet and screw M5x20mm || 05&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Protective cap || 01&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | User manual || 01&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&lt;br /&gt;
=== Technical specifications ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! LIGO AIR LxS || LIGO AIR LxT !! LIGO AIR-PRO&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.4, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | TX power max || 6 dBm || 6 dBm || 19.5 dBm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Advertising Protocol || colspan=&amp;quot;3&amp;quot; | - SOJI Protocol (default)&amp;lt;br&amp;gt;- Mielta Protocol (setting in mobile app)&amp;lt;br&amp;gt;- Escort Protocol (setting in mobile app)&lt;br /&gt;
- Technoton Protocol (setting in mobile app) &lt;br /&gt;
- EUROSEN Protocol (setting in mobile app)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply: Built-in Battery Li-SOCL2 || 3.6V * 2600mAh || 3.6V * 2600mAh || 3.6V * 3600mAh&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Current consumption in working mode (uA) || 9 || 9 || 27&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measurement interval (second) || colspan=&amp;quot;3&amp;quot; | From 5 to 10&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Advertising message interval (second) || From 5 to 10 || From 5 to 10 || 10&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature (°C) || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution (bit) || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Accelerometer || N/A || Built-in&lt;br /&gt;
|Built-in&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Memory || colspan=&amp;quot;3&amp;quot; | Built-in 32Mb EEPROM&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 5 to 60&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±2&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || colspan=&amp;quot;3&amp;quot; | 10 years&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Storage condition || colspan=&amp;quot;3&amp;quot; | 15°C to 30°C, RH &amp;lt; 60%&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Overview of sensor structure ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig1-sensor-structure.png|frameless|674px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. LIGO AIR series&#039; overall dimensions (mm).&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Manufacturer part number !! L (mm)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR – L7S&amp;lt;br&amp;gt;LIGO AIR – L7T&amp;lt;br&amp;gt;LIGO AIR PRO – L7S || 700 ± 3&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR – L10S&amp;lt;br&amp;gt;LIGO AIR – L10T&amp;lt;br&amp;gt;LIGO AIR PRO – L10S || 1000 ± 3&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR – L15S&amp;lt;br&amp;gt;LIGO AIR – L15T&amp;lt;br&amp;gt;LIGO AIR PRO – L15S || 1500 ± 3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Fuel tank preparation ===&lt;br /&gt;
&lt;br /&gt;
# Empty the tank, clean and dry it if necessary, for gasoline fuel tanks, all fuel vapors must be removed from the tank to prevent fire or explosion during installation.&lt;br /&gt;
# Selecting the installation location for the LIGO AIR fuel level sensor, take the following requirements into account:&lt;br /&gt;
#* The sensor&#039;s installation location must be as close as possible to the tank&#039;s geometric center and at its deepest point.&lt;br /&gt;
#* The sensor&#039;s installation location must be as close as possible to the tank&#039;s geometric center and at its deepest point.&lt;br /&gt;
#* Ensure that the sensor must avoid contact with reinforcement ribs and other equipment inside the tank.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig2-tank-preparation.png|frameless|600px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Tank installation location requirements&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
3. Drilling a central hole (Ø42 mm), then drill four additional holes for mounting screws as shown in the figure below:&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig3-drilling.png|frameless|523px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Drilling central hole Ø42 mm and four mounting screw holes&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Notes:&#039;&#039;&#039; The mounting hole diameter depends on the material and thickness of the tank&lt;br /&gt;
* For fuel tanks with a wall thickness ≤3 mm, M5 rivet nut should be used. In this case, Drill 4 × Ø7 mm mounting screw holes.&lt;br /&gt;
* For fuel tanks with a wall thickness &amp;gt;3 mm, M4.8 self-tapping screw can be used. In this case, Drill 4 × Ø7 mm mounting screw holes.&lt;br /&gt;
* Mounting holes positions must ensure that the sensor is installed in the direction of maximum signal transmission (the directional arrow on the sensor cover should point toward the receiving device).&lt;br /&gt;
&lt;br /&gt;
=== Sensor preparation ===&lt;br /&gt;
&lt;br /&gt;
1. Cutting sensor tube&lt;br /&gt;
&lt;br /&gt;
The length of the sensor tube should be calculated according to the following formula:&lt;br /&gt;
&lt;br /&gt;
  &#039;&#039;&#039;L = H - 25 mm&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* &#039;&#039;&#039;L&#039;&#039;&#039; – final tube length after cutting&lt;br /&gt;
* &#039;&#039;&#039;H&#039;&#039;&#039; - height of the tank at the installation point.&lt;br /&gt;
&lt;br /&gt;
After cutting, remove any burrs and clean the tube using fuel.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; The minimum length of the tube after cutting must be at least 150 mm (15cm)&lt;br /&gt;
&lt;br /&gt;
2. Installing bottom-stop springs and filter&lt;br /&gt;
&lt;br /&gt;
Installing the bottom-stop and the oil filter in sequence, as shown in the figure below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Screenshot 2026-06-19 161113.png|frameless|562px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Installing bottom-stop springs and oil filter&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing sensor on the fuel tank ===&lt;br /&gt;
&lt;br /&gt;
# Apply gasket sealant to fuel tank, gasket and sensor flange (using Fuel resistant RTV Silicone gasket maker).&lt;br /&gt;
# Insert the LIGO AIR fuel sensor into the tank and secure fasten it securely using the appropriate screws (using self-tapping screws or rivet nut depending on fuel tank thickness and material as mentioned above).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig5-installing-on-tank.png|frameless|624px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. Installing sensor on the fuel tank&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Install the sensor in the direction of maximum signal transmission (the directional arrow on the sensor cover should point toward the receiving device).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig6-clearance.png|frameless|540px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 6. Minimum 150 mm clearance between sensor top and overhead metal surface&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Ensure a minimum clearance of 150 mm between the top of the sensor and any overhead metal surface.&lt;br /&gt;
&lt;br /&gt;
=== Installing protective cap ===&lt;br /&gt;
&lt;br /&gt;
The protective cap should be installed after the sensor installation and calibration processes are completed. This cap helps prevent unauthorized access and protects the sensor from environmental factors such as sunlight, physical impact…&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig7-protective-cap-install.png|frameless|537px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 7. Installing protective cap&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Push the protective cap until it clicks into place, indicating that it is fully seated.&lt;br /&gt;
&lt;br /&gt;
In cases when sensor inspection is required, the protective cap must be removed. Follow the removal procedure as shown in picture below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig8-protective-cap-remove.png|frameless|651px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 8. Protective cap removal procedure&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Please ensure that no parts of the sensor are damaged while removing the protective cap.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== Connecting sensor to smartphone and setting ===&lt;br /&gt;
&lt;br /&gt;
To configure the LIGO AIR fuel sensor, download the &#039;&#039;&#039;LIGO BLE Configurator&#039;&#039;&#039; app on &#039;&#039;&#039;Appstore (for IOS) or CH Play (for Android devices).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hereinafter LIGO BLE Configurator is referred to as the &amp;quot;application&amp;quot; or &amp;quot;app&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; For full operation, you must grant all the permissions requested by the app&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-AIR-fig9-app-permissions-1.jpg|Figure 9. Grant all the permissions request by program&lt;br /&gt;
File:LIGO-AIR-fig10-app-permissions-2.jpg|Figure 10. Grant all the permissions request by program&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Grant all the permissions request by program as above&lt;br /&gt;
&lt;br /&gt;
==== Connecting sensor ====&lt;br /&gt;
&lt;br /&gt;
Run the application and select the sensor you want to connect to, as shown in the pictures below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-AIR-fig11-connect-1.jpg|Figure 11. Selecting sensor type&lt;br /&gt;
File:LIGO-AIR-fig12-connect-2.jpg|Figure 12. Connecting sensor&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set Empty and Set Full ====&lt;br /&gt;
&lt;br /&gt;
Step 1: Set Full&lt;br /&gt;
&lt;br /&gt;
* Immerse the sensor in fuel until the fuel reaches the sensor flange&lt;br /&gt;
* Wait for C_working value to stablize and then press Set Full button&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig13-set-full.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 13. Set Full&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 2: Set Empty&lt;br /&gt;
&lt;br /&gt;
* Remove the sensor from the fuel and wait for 1-2 minutes allow the fuel drain from the measuring probe and then press Set Empty button.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig14-set-empty.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 14. Set Empty&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Calibration table ===&lt;br /&gt;
&lt;br /&gt;
After installation, the sensor must be calibrated to ensure accurate volume measurement. Calibration table can be performed either on the platform (server) or locally on the sensor.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Platform (Server):&#039;&#039;&#039; The sensor sends raw level values (ranging from 0 to 4095) to the server via the telematic device. The platform uses a calibration table to convert these values into volume (liters).&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Sensor:&#039;&#039;&#039; The sensor directly sends volume values (liters) based on the internal calibration table.&lt;br /&gt;
&lt;br /&gt;
Note: For LIGO AIR sensors, the manufacturer recommends performing calibration table on the platform.&lt;br /&gt;
&lt;br /&gt;
== Operation ==&lt;br /&gt;
&lt;br /&gt;
=== Solutions for long-range or in metal-obstructed environments ===&lt;br /&gt;
&lt;br /&gt;
In some cases, when the distance between the sensor and the receiver is long or there are many metal obstacles causing signal attenuation, customers can choose the LIGO AIR PRO model to increase transmission power or install the LIGO AIR Adapter at the receiver side to boost the signal received from the sensor.&lt;br /&gt;
&lt;br /&gt;
Below is a comparison table of transmission distances between different types of sensors and receivers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Device !! Bluetooth signal range&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR + Telematics (tracker) device || Standard distance&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR + LIGO AIR Adapter || 2 x Standard distance&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR Pro + LIGO AIR Adapter || 4 x Standard distance&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Symptom !! Possible Cause !! Solution&lt;br /&gt;
|-&lt;br /&gt;
| Sensor not discovered by LIGO BLE Configurator app&lt;br /&gt;
|&lt;br /&gt;
* App permissions not granted&lt;br /&gt;
* Sensor outside Bluetooth range&lt;br /&gt;
* Metal obstruction blocking sensor head&lt;br /&gt;
* Sensor battery depleted (end of service life)&lt;br /&gt;
|&lt;br /&gt;
* Re-grant Bluetooth &amp;amp; location permissions&lt;br /&gt;
* Move within range; verify line-of-sight&lt;br /&gt;
* Remove any metal plate above sensor&lt;br /&gt;
* Verify sensor age vs ~10-year service life&lt;br /&gt;
|-&lt;br /&gt;
| RSSI at receiver below −80 dBm&lt;br /&gt;
|&lt;br /&gt;
* Distance too far&lt;br /&gt;
* Metal obstruction in signal path&lt;br /&gt;
* Sensor orientation wrong (arrow not toward receiver)&lt;br /&gt;
|&lt;br /&gt;
* Use LIGO AIR PRO or add LIGO AIR Adapter&lt;br /&gt;
* Re-route or remove obstruction&lt;br /&gt;
* Re-mount sensor so directional arrow points to receiver&lt;br /&gt;
|-&lt;br /&gt;
| Reading shows 0% after Set Empty when fuel is present&lt;br /&gt;
|&lt;br /&gt;
* Set Empty / Set Full procedure executed in wrong order&lt;br /&gt;
* Tube damaged or burrs not removed&lt;br /&gt;
|&lt;br /&gt;
* Repeat calibration: Set Full first (immersed), then Set Empty (drained)&lt;br /&gt;
* Inspect tube, deburr, re-cut if necessary&lt;br /&gt;
|-&lt;br /&gt;
| Cannot connect with default password (login refused)&lt;br /&gt;
|&lt;br /&gt;
* Default password already changed by previous user&lt;br /&gt;
|&lt;br /&gt;
* Contact the previous installer&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
If the problem persists, contact [[#Support / Contact|Support]].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Maintenance ==&lt;br /&gt;
&lt;br /&gt;
== Certification / Compliance ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[LIGO AIR Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Channel !! Detail&lt;br /&gt;
|-&lt;br /&gt;
| Company || SOJI ELECTRONICS.,JSC&lt;br /&gt;
|-&lt;br /&gt;
| Address || C50-LK13, Lot No. 09, New Urban Area, Le Trong Tan street, Duong Noi Ward, Ha Dong District, Hanoi City, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Tel/Fax || +84 24 62 932 369&lt;br /&gt;
|-&lt;br /&gt;
| Email || contact@sojielectronics.com&lt;br /&gt;
|-&lt;br /&gt;
| Website || [http://sojielectronics.com sojielectronics.com]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Datasheet&amp;diff=2069</id>
		<title>LIGO AIR Datasheet</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Datasheet&amp;diff=2069"/>
		<updated>2026-06-26T01:52:05Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Technical Specifications */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Product Overview ==&lt;br /&gt;
&lt;br /&gt;
LIGO-AIR Fuel Level Sensor is developed and manufactured by SOJI Electronics JSC. The device is designed to measure the level of liquid fuel and other non-conductive liquids in vehicle tanks and stationary fuel storage, applicable in different fields. Equipped with advanced Bluetooth Low Energy technology, LIGO AIR enables seamless, real-time data transmission without the need for complex wiring and easy for installation.&lt;br /&gt;
&lt;br /&gt;
The purpose of creating a new product was to satisfy the increasing demand of the transport telematics market for improved reliability of the design, data, and ease of installation. Unlike its predecessors, the wireless fuel level sensor helps minimize the risk of signal wire tampering.&lt;br /&gt;
&lt;br /&gt;
In addition, the heavy-duty metal structure helps increase protection against environmental factors such as dust, moisture, vibration and high temperature conditions. This ensures that the LIGO AIR sensor maintains stability, durability, and operational lifespan equal to or even greater than that of wired sensors.&lt;br /&gt;
&lt;br /&gt;
== Key Features ==&lt;br /&gt;
&lt;br /&gt;
* High accuracy up to 99,5%.&lt;br /&gt;
* By utilizing directional antenna technology, the sensor achieves a communication range up to 1.5 times greater than typical market-available sensors.&lt;br /&gt;
* With ultra-low power consumption (9uA in working mode and 4uA in storage mode), the sensor ensures longer battery life and reduced maintenance.&lt;br /&gt;
* Can be optionally cut off or prolonged up to 6000mm.&lt;br /&gt;
* Wide operating temperature ranges from -40°C to +85°C.&lt;br /&gt;
* TX power up to 19.5dBm (LIGO AIR PRO).&lt;br /&gt;
* Battery capacity up to 3600mAh (optional) helps to extend battery life of sensor.&lt;br /&gt;
* Replaceable battery.&lt;br /&gt;
* Built-in accelerometer.&lt;br /&gt;
* Built-in 32Mb EEPROM for data logs (Continuously stored for a minimum of 90 days) and setting logs (Maximum 500 logs).&lt;br /&gt;
* Built-in Anti-mud oil filter and anti-vibration springs.&lt;br /&gt;
* IP69K waterproof standard.&lt;br /&gt;
* 8-10 years of battery life.&lt;br /&gt;
&lt;br /&gt;
== Applications ==&lt;br /&gt;
&lt;br /&gt;
* Trucks, excavators, heavy construction machines…&lt;br /&gt;
* Marine vessels and electric generators.&lt;br /&gt;
* Off-highway machinery including high-temperature &amp;amp; vibration environments.&lt;br /&gt;
* Industrial oil storage tanks or stationary storage tanks.&lt;br /&gt;
&lt;br /&gt;
== Product Variants / Ordering Information ==&lt;br /&gt;
&lt;br /&gt;
During our discussions with customers and market research, we found that most wireless fuel level sensors on the market suffer from key issues such as poor durability, short battery life (2-4 years), and unstable connectivity (Bluetooth signal).&lt;br /&gt;
&lt;br /&gt;
To address these challenges, the LIGO AIR series was developed — a new generation of wireless fuel level sensors engineered for superior reliability, long battery life, and stable long-range communication in all environments.&lt;br /&gt;
&lt;br /&gt;
The LIGO AIR series offers three specialized versions, each optimized to meet specific operational demands — from cost efficiency to long-range communication and extended battery life as bellow:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LIGO AIR LxS:&#039;&#039;&#039; The standard version of the LIGO AIR sensor is designed as a cost-effective solution that fulfills essential monitoring needs while maintaining reliable performance (Trucks, excavators, cement mixer truck, heavy construction machines…)&lt;br /&gt;
* &#039;&#039;&#039;LIGO AIR LxT:&#039;&#039;&#039; The extended-battery version of the LIGO AIR sensor is designed for applications that demand long operational life (8-12 years) and reliable performance in harsh environments, including high-temperature conditions.&lt;br /&gt;
* &#039;&#039;&#039;LIGO AIR PRO:&#039;&#039;&#039; The long-range version of the LIGO AIR sensor is designed for applications requiring extended transmission distances or operation in environments with significant obstacles (Marine vessels, electric generators, Industrial oil storage tanks, stationary storage tanks…)&lt;br /&gt;
&lt;br /&gt;
The following table summarizes the performance differences between LIGO AIR models and competitive products in terms of battery life and transmission distance:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! rowspan=&amp;quot;2&amp;quot; | Comparison product&lt;br /&gt;
! Competitive products on the market&lt;br /&gt;
! Standard version&lt;br /&gt;
! Extended battery life&lt;br /&gt;
! Extended transmission range&lt;br /&gt;
|-&lt;br /&gt;
! Product&lt;br /&gt;
! LIGO AIR LxS&lt;br /&gt;
! LIGO AIR LxT&lt;br /&gt;
! LIGO AIR Pro LxS&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Battery life || 0.4 x Standard (2-4 years) || Standard (6-8 years) || 1.5x Standard (8-12 years) || Standard (6-8 years)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Transmission range || 0.6 x Standard || Standard || Standard || 2x standard&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Technical Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! LIGO AIR LxS !! LIGO AIR LxT !! LIGO AIR-PRO&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.4, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | TX power max || 6 dBm || 6 dBm || 19.5 dBm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Advertising Protocol || colspan=&amp;quot;3&amp;quot; | - SOJI Protocol (default)&amp;lt;br&amp;gt;- Mielta Protocol (setting in mobile app)&amp;lt;br&amp;gt;- Escort Protocol (setting in mobile app)&lt;br /&gt;
- Technoton Protocol (setting in mobile app)&lt;br /&gt;
&lt;br /&gt;
- Eurosen Protocol (setting in mobile app)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply || 3.6V * 2600mAh || 3.6V * 2400mAh || 3.6V * 3600mAh&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Current consumption in working mode (uA) || 9 || 9 || 27&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measurement interval (second) || colspan=&amp;quot;3&amp;quot; | From 5 to 10&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Advertising message interval (second) || From 5 to 10 || From 5 to 10 || From 10 to 15&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature (°C) || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution (bit) || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Accelerometer || N/A || Built-in || Built-in&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Memory || colspan=&amp;quot;3&amp;quot; | Built-in 32Mb EEPROM&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 5 to 60&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±2&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || colspan=&amp;quot;3&amp;quot; | 10 years&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Dimensions ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig1-dimensions.png|frameless|674px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. LIGO AIR series&#039; overall dimensions (mm).&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Manufacturer part number !! L (mm)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR – L7S&amp;lt;br&amp;gt;LIGO AIR – L7T&amp;lt;br&amp;gt;LIGO AIR PRO – L7S || 700 ± 3&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR – L10S&amp;lt;br&amp;gt;LIGO AIR – L10T&amp;lt;br&amp;gt;LIGO AIR PRO – L10S || 1000 ± 3&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR – L15S&amp;lt;br&amp;gt;LIGO AIR – L15T&amp;lt;br&amp;gt;LIGO AIR PRO – L15S || 1500 ± 3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Compliance and Certifications ==&lt;br /&gt;
Refer to [[LIGO AIR Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Warranty ===&lt;br /&gt;
&lt;br /&gt;
* The warranty period is 3 years from the date of shipment by the manufacturer.&lt;br /&gt;
* The warranty period and service life do not apply to the battery.&lt;br /&gt;
* Compliance with the specified operating, transport, and storage conditions is a prerequisite for the manufacturer&#039;s guarantee.&lt;br /&gt;
* The manufacturer reserves the right to modify the design and composition of the product without prior notice to the customer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Datasheet&amp;diff=2068</id>
		<title>LIGO AIR Datasheet</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Datasheet&amp;diff=2068"/>
		<updated>2026-06-26T01:50:00Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Technical Specifications */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Product Overview ==&lt;br /&gt;
&lt;br /&gt;
LIGO-AIR Fuel Level Sensor is developed and manufactured by SOJI Electronics JSC. The device is designed to measure the level of liquid fuel and other non-conductive liquids in vehicle tanks and stationary fuel storage, applicable in different fields. Equipped with advanced Bluetooth Low Energy technology, LIGO AIR enables seamless, real-time data transmission without the need for complex wiring and easy for installation.&lt;br /&gt;
&lt;br /&gt;
The purpose of creating a new product was to satisfy the increasing demand of the transport telematics market for improved reliability of the design, data, and ease of installation. Unlike its predecessors, the wireless fuel level sensor helps minimize the risk of signal wire tampering.&lt;br /&gt;
&lt;br /&gt;
In addition, the heavy-duty metal structure helps increase protection against environmental factors such as dust, moisture, vibration and high temperature conditions. This ensures that the LIGO AIR sensor maintains stability, durability, and operational lifespan equal to or even greater than that of wired sensors.&lt;br /&gt;
&lt;br /&gt;
== Key Features ==&lt;br /&gt;
&lt;br /&gt;
* High accuracy up to 99,5%.&lt;br /&gt;
* By utilizing directional antenna technology, the sensor achieves a communication range up to 1.5 times greater than typical market-available sensors.&lt;br /&gt;
* With ultra-low power consumption (9uA in working mode and 4uA in storage mode), the sensor ensures longer battery life and reduced maintenance.&lt;br /&gt;
* Can be optionally cut off or prolonged up to 6000mm.&lt;br /&gt;
* Wide operating temperature ranges from -40°C to +85°C.&lt;br /&gt;
* TX power up to 19.5dBm (LIGO AIR PRO).&lt;br /&gt;
* Battery capacity up to 3600mAh (optional) helps to extend battery life of sensor.&lt;br /&gt;
* Replaceable battery.&lt;br /&gt;
* Built-in accelerometer.&lt;br /&gt;
* Built-in 32Mb EEPROM for data logs (Continuously stored for a minimum of 90 days) and setting logs (Maximum 500 logs).&lt;br /&gt;
* Built-in Anti-mud oil filter and anti-vibration springs.&lt;br /&gt;
* IP69K waterproof standard.&lt;br /&gt;
* 8-10 years of battery life.&lt;br /&gt;
&lt;br /&gt;
== Applications ==&lt;br /&gt;
&lt;br /&gt;
* Trucks, excavators, heavy construction machines…&lt;br /&gt;
* Marine vessels and electric generators.&lt;br /&gt;
* Off-highway machinery including high-temperature &amp;amp; vibration environments.&lt;br /&gt;
* Industrial oil storage tanks or stationary storage tanks.&lt;br /&gt;
&lt;br /&gt;
== Product Variants / Ordering Information ==&lt;br /&gt;
&lt;br /&gt;
During our discussions with customers and market research, we found that most wireless fuel level sensors on the market suffer from key issues such as poor durability, short battery life (2-4 years), and unstable connectivity (Bluetooth signal).&lt;br /&gt;
&lt;br /&gt;
To address these challenges, the LIGO AIR series was developed — a new generation of wireless fuel level sensors engineered for superior reliability, long battery life, and stable long-range communication in all environments.&lt;br /&gt;
&lt;br /&gt;
The LIGO AIR series offers three specialized versions, each optimized to meet specific operational demands — from cost efficiency to long-range communication and extended battery life as bellow:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;LIGO AIR LxS:&#039;&#039;&#039; The standard version of the LIGO AIR sensor is designed as a cost-effective solution that fulfills essential monitoring needs while maintaining reliable performance (Trucks, excavators, cement mixer truck, heavy construction machines…)&lt;br /&gt;
* &#039;&#039;&#039;LIGO AIR LxT:&#039;&#039;&#039; The extended-battery version of the LIGO AIR sensor is designed for applications that demand long operational life (8-12 years) and reliable performance in harsh environments, including high-temperature conditions.&lt;br /&gt;
* &#039;&#039;&#039;LIGO AIR PRO:&#039;&#039;&#039; The long-range version of the LIGO AIR sensor is designed for applications requiring extended transmission distances or operation in environments with significant obstacles (Marine vessels, electric generators, Industrial oil storage tanks, stationary storage tanks…)&lt;br /&gt;
&lt;br /&gt;
The following table summarizes the performance differences between LIGO AIR models and competitive products in terms of battery life and transmission distance:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! rowspan=&amp;quot;2&amp;quot; | Comparison product&lt;br /&gt;
! Competitive products on the market&lt;br /&gt;
! Standard version&lt;br /&gt;
! Extended battery life&lt;br /&gt;
! Extended transmission range&lt;br /&gt;
|-&lt;br /&gt;
! Product&lt;br /&gt;
! LIGO AIR LxS&lt;br /&gt;
! LIGO AIR LxT&lt;br /&gt;
! LIGO AIR Pro LxS&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Battery life || 0.4 x Standard (2-4 years) || Standard (6-8 years) || 1.5x Standard (8-12 years) || Standard (6-8 years)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Transmission range || 0.6 x Standard || Standard || Standard || 2x standard&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Technical Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! LIGO AIR LxS !! LIGO AIR LxT) !! LIGO AIR-PRO&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500...up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.4, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | TX power max || colspan=&amp;quot;2&amp;quot; | 6 dBm || 19.5 dBm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Advertising Protocol || colspan=&amp;quot;3&amp;quot; | - SOJI Protocol (default)&amp;lt;br&amp;gt;- Mielta Protocol (setting in mobile app)&amp;lt;br&amp;gt;- Escort Protocol (setting in mobile app)&lt;br /&gt;
- Technoton Protocol (setting in mobile app)&lt;br /&gt;
&lt;br /&gt;
- Eurosen Protocol (setting in mobile app)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply || 3.6V * 2600mAh || 3.6V * 2400mAh || 3.6V * 3600mAh&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Current consumption in working mode (uA) || colspan=&amp;quot;2&amp;quot; | 9 || 27&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measurement interval (second) || colspan=&amp;quot;3&amp;quot; | From 5 to 10&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Advertising message interval (second) || colspan=&amp;quot;2&amp;quot; | From 5 to 10 || From 10 to 15&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature (°C) || colspan=&amp;quot;3&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution (bit) || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Accelerometer || N/A || built-in || Built-in&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Memory || colspan=&amp;quot;3&amp;quot; | Built-in 32Mb EEPROM&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 5 to 60&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement within the entire temperature measuring range, °С || colspan=&amp;quot;3&amp;quot; | ±2&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || colspan=&amp;quot;3&amp;quot; | 10 years&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Dimensions ===&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig1-dimensions.png|frameless|674px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. LIGO AIR series&#039; overall dimensions (mm).&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Manufacturer part number !! L (mm)&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR – L7S&amp;lt;br&amp;gt;LIGO AIR – L7T&amp;lt;br&amp;gt;LIGO AIR PRO – L7S || 700 ± 3&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR – L10S&amp;lt;br&amp;gt;LIGO AIR – L10T&amp;lt;br&amp;gt;LIGO AIR PRO – L10S || 1000 ± 3&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | LIGO AIR – L15S&amp;lt;br&amp;gt;LIGO AIR – L15T&amp;lt;br&amp;gt;LIGO AIR PRO – L15S || 1500 ± 3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Compliance and Certifications ==&lt;br /&gt;
Refer to [[LIGO AIR Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Warranty ===&lt;br /&gt;
&lt;br /&gt;
* The warranty period is 3 years from the date of shipment by the manufacturer.&lt;br /&gt;
* The warranty period and service life do not apply to the battery.&lt;br /&gt;
* Compliance with the specified operating, transport, and storage conditions is a prerequisite for the manufacturer&#039;s guarantee.&lt;br /&gt;
* The manufacturer reserves the right to modify the design and composition of the product without prior notice to the customer.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_Protocol&amp;diff=2067</id>
		<title>LIGO PRO Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_Protocol&amp;diff=2067"/>
		<updated>2026-06-24T10:57:09Z</updated>

		<summary type="html">&lt;p&gt;Admin: Initial Release [release]&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LLS Protocol is used in LIGO RS232 / RS485 fuel level sensor with the following parameters:&lt;br /&gt;
&lt;br /&gt;
* Baud rate: 2400,4800,9600,19200,38400,115200 (can be configured by software on PC)&lt;br /&gt;
* Data bits: 8&lt;br /&gt;
* Parity: None&lt;br /&gt;
* Stop bits: 1&lt;br /&gt;
* Flow control: None&lt;br /&gt;
&lt;br /&gt;
LIGO fuel level sensor has 2 working modes:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Slave mode:&#039;&#039;&#039; In this mode, the sensor will respond to all requests from the external device (master device). Each sensor in the network will be distinguished by different addresses configured with the LIGO Configurator software&lt;br /&gt;
# &#039;&#039;&#039;Master mode:&#039;&#039;&#039; In this mode the sensor will automatically send data to the external device with a preset interval (message interval)&lt;br /&gt;
&lt;br /&gt;
To enable this mode on the LIGO Configurator software, do the following:&lt;br /&gt;
&lt;br /&gt;
* Automatic transmission: select HEX (binary) or ASCII or ASCII EXT&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Automatic-Transmission.png|frameless|219px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Automatic transmission setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Set the time of each transfer (Message interval)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Message-Interval.png|frameless|228px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Message interval setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Exchange Protocol Description ==&lt;br /&gt;
&lt;br /&gt;
The LLS protocol supports two types of exchange protocols: binary (HEX) and character view (transmission of ASCII sequences). It is recommended to use binary exchange protocol.&lt;br /&gt;
&lt;br /&gt;
=== Format of Binary Protocol Messages ===&lt;br /&gt;
&lt;br /&gt;
All the commands of the binary communication protocol have the same standardized format which is given in the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequential number of the field !! Field name !! Field size, byte !! Description&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 1 || Prefix || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is a marker of the message beginning shall have prefix 31h, and an outcoming messages shall be displayed with 3Eh prefix by the program.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 2 || Network address || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the network address of the recipient. &amp;lt;br&amp;gt;For prefix 3Eh specify the network address of the sender.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 3 || Operation code || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the code of operation which the program shall perform For Eh prefix specify the code of operation to which the response is given&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 4 || Data || style=&amp;quot;text-align:center;&amp;quot; | It depends on the operation code || Data composition and format of the field depends on the operation code&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 5 || Checksum || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is used to control over data integrity&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Single-Stage Data Reading (command 06h) ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The data are transmitted with a lower byte ahead.&lt;br /&gt;
&lt;br /&gt;
==== Command format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output (command 07h) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to switch on periodic data output.&lt;br /&gt;
&lt;br /&gt;
After the command is processed, the sensor starts sending data periodically — level, temperature, and frequency — with the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power power supply (if the data output mode is not istalled by default).&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Periodic data output format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output Interval Adjustment (13h command) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to set up interval of periodic data output.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 0…255 || style=&amp;quot;text-align:left;&amp;quot; | Interval of the data output in seconds&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Default Data Output Mode (command 17h) ===&lt;br /&gt;
&lt;br /&gt;
This command determines the order of data output after the sensor is powered on or the processor is reset. After the power is on or the processor is reset, the program will send data periodically via the interface at the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | +3 || rowspan=&amp;quot;4&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in binary form&lt;br /&gt;
|-&lt;br /&gt;
| 02h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII mode)&lt;br /&gt;
|-&lt;br /&gt;
| 03h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII EXT mode)&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h…FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Description of Commands for the Text-Based Protocol ==&lt;br /&gt;
&lt;br /&gt;
Data exchange via the text-based protocol includes receipt and sending of ASCII symbols sequence interpreted and the request and response commands.&lt;br /&gt;
&lt;br /&gt;
=== Reading the Data ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The command is a sequence of symbols ASCII &amp;quot;D&amp;quot; and &amp;quot;O&amp;quot;. After receipt of the &amp;quot;DO&amp;quot; command the program will response in the form of ASCII symbols sequence.&lt;br /&gt;
&lt;br /&gt;
For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. All values are in hexadecimal form.&lt;br /&gt;
&lt;br /&gt;
In case the frequency value exceeds FFFh, the data are considered invalid.&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output ===&lt;br /&gt;
&lt;br /&gt;
The command is designed to switch on periodic data output. After processing the command, the sensor performs periodic data output in the text-based form (ASCII codes) of the following data: relative level, temperature, frequency.&lt;br /&gt;
&lt;br /&gt;
The data are being output periodically with an interval set up when cofiguring the sensor (LIGO Configurator software). In case the data output interval is set to zero, the data output won&#039;t be performed.&lt;br /&gt;
&lt;br /&gt;
Switching on of the periodic data output is done by sending of the &amp;quot;DP&amp;quot; symbols in line. After processing of the command, the symbols line will be received. For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power supply.&lt;br /&gt;
&lt;br /&gt;
== Checksum Calculation Algorithm ==&lt;br /&gt;
&lt;br /&gt;
The checksum is calculated using Dallas APPLICATION NOTE 27 table method: &#039;&#039;Understanding and Using Cyclic Redundancy Checks with Dallas Semiconductor iButton Products&#039;&#039;. One can use the following algorithms to calculate the checksum with a polynom &amp;lt;code&amp;gt;x^8 + x^5 + x^4 + 1&amp;lt;/code&amp;gt; (C language):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
U8 CRC8(U8 data, U8 crc)&lt;br /&gt;
{&lt;br /&gt;
    U8 i = data ^ crc;&lt;br /&gt;
    crc = 0;&lt;br /&gt;
    if (i &amp;amp; 0x01) crc ^= 0x5e;&lt;br /&gt;
    if (i &amp;amp; 0x02) crc ^= 0xbc;&lt;br /&gt;
    if (i &amp;amp; 0x04) crc ^= 0x61;&lt;br /&gt;
    if (i &amp;amp; 0x08) crc ^= 0xc2;&lt;br /&gt;
    if (i &amp;amp; 0x10) crc ^= 0x9d;&lt;br /&gt;
    if (i &amp;amp; 0x20) crc ^= 0x23;&lt;br /&gt;
    if (i &amp;amp; 0x40) crc ^= 0x46;&lt;br /&gt;
    if (i &amp;amp; 0x80) crc ^= 0x8c;&lt;br /&gt;
    return crc;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_Protocol&amp;diff=2066</id>
		<title>Category:LIGO PRO Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_Protocol&amp;diff=2066"/>
		<updated>2026-06-24T10:57:01Z</updated>

		<summary type="html">&lt;p&gt;Admin: Auto-created category for LIGO PRO Protocol&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- auto-created category --&amp;gt;&lt;br /&gt;
{{DEFAULTSORT:05 LIGO PRO Protocol}}&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_Protocol&amp;diff=2065</id>
		<title>LIGO PRO Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_Protocol&amp;diff=2065"/>
		<updated>2026-06-24T10:57:01Z</updated>

		<summary type="html">&lt;p&gt;Admin: Create page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brief introduction to the protocol per IEC/IEEE standards for wireless data protocols.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Protocol name and version&#039;&#039;&#039; — full name + version number (vd: SOJI Protocol v2.1)&lt;br /&gt;
* &#039;&#039;&#039;Purpose&#039;&#039;&#039; — what data the protocol transmits and to whom&lt;br /&gt;
* &#039;&#039;&#039;Transport layer&#039;&#039;&#039; — Bluetooth 5.4 BLE / WiFi / LoRa / proprietary RF&lt;br /&gt;
* &#039;&#039;&#039;Frequency &amp;amp; modulation&#039;&#039;&#039; — 2.4 GHz GFSK / LoRa CSS / ...&lt;br /&gt;
* &#039;&#039;&#039;Operating mode&#039;&#039;&#039; — Advertising-only (one-way broadcast) / Connection-based / Mesh&lt;br /&gt;
* &#039;&#039;&#039;Compatible devices&#039;&#039;&#039; — list of sensors / receivers supporting this protocol&lt;br /&gt;
* &#039;&#039;&#039;Reference standards&#039;&#039;&#039; — IEEE 802.15.4, Bluetooth Core 5.4, etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example structure:&#039;&#039;&#039;&lt;br /&gt;
* Paragraph 1: &amp;quot;The [Protocol Name] is a [type] protocol defined by SOJI Electronics for [purpose]...&amp;quot;&lt;br /&gt;
* Paragraph 2: &amp;quot;It operates over [transport] at [frequency] and is supported by [device list]...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Optional sub-sections:&#039;&#039;&#039;&lt;br /&gt;
* === Protocol Stack === — layered diagram of protocol stack&lt;br /&gt;
* === Roles === — Broadcaster / Observer / Central / Peripheral&lt;br /&gt;
* === Security === — encryption, authentication, password protection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advertising Message Format ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Byte-by-byte breakdown of the BLE advertising packet payload.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Packet overview&#039;&#039;&#039; — total packet length, manufacturer ID, structure summary&lt;br /&gt;
* &#039;&#039;&#039;Field table&#039;&#039;&#039; — every field with offset, length, value, description&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example field breakdown table:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Length (bytes) !! Field Name !! Value / Format !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || 1 || AD Length || 0xXX || Length of the advertising data structure&lt;br /&gt;
|-&lt;br /&gt;
| 0x01 || 1 || AD Type || 0xFF || Manufacturer Specific Data (per Bluetooth Core spec)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02 || 2 || Company ID || 0xXXXX (LE) || SOJI Electronics SIG-assigned Company ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x04 || 1 || Protocol ID || 0xXX || SOJI Protocol identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x05 || 1 || Frame Type || 0xXX || Frame type (data frame, status frame, ...)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06 || N || Payload || ... || Sensor data payload (see Payload Structure below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06+N || 2 || CRC-16 || 0xXXXX || CRC-16/CCITT checksum over Offset 0x00 to 0x05+N&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sub-sections to consider:&#039;&#039;&#039;&lt;br /&gt;
* === Packet Overview === — total length, byte order (LE/BE), encoding&lt;br /&gt;
* === Field Definitions === — main field table above&lt;br /&gt;
* === Payload Structure === — detailed payload fields (level, temperature, battery, RSSI, etc.) with units and scaling&lt;br /&gt;
* === Frame Types === — enumeration of frame types if multiple exist&lt;br /&gt;
* === CRC / Checksum === — algorithm (CRC-16/CCITT, polynomial, init value, XOR-out)&lt;br /&gt;
* === Example Raw Frame === — hex dump of a real advertising packet with field annotations&lt;br /&gt;
* === Decoding Pseudocode === — short code snippet (C / Python) showing how to parse a frame&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example raw frame block:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Raw bytes (hex):&lt;br /&gt;
  0E FF 5A A5 01 02 03 04 ...&lt;br /&gt;
&lt;br /&gt;
Decoded:&lt;br /&gt;
  AD Length    = 0x0E (14 bytes)&lt;br /&gt;
  AD Type      = 0xFF (Manufacturer Specific)&lt;br /&gt;
  Company ID   = 0xA55A (SOJI Electronics)&lt;br /&gt;
  Protocol ID  = 0x01 (SOJI Protocol)&lt;br /&gt;
  Frame Type   = 0x02 (Data Frame)&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example decoding pseudocode:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def decode_soji_frame(adv_data: bytes) -&amp;gt; dict:&lt;br /&gt;
    if adv_data[1] != 0xFF:&lt;br /&gt;
        return None&lt;br /&gt;
    company_id = int.from_bytes(adv_data[2:4], &#039;little&#039;)&lt;br /&gt;
    protocol_id = adv_data[4]&lt;br /&gt;
    frame_type = adv_data[5]&lt;br /&gt;
    payload = adv_data[6:-2]&lt;br /&gt;
    crc = int.from_bytes(adv_data[-2:], &#039;little&#039;)&lt;br /&gt;
    # ... validate CRC, parse payload&lt;br /&gt;
    return { ... }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Protocol&amp;diff=2064</id>
		<title>LIGO PRO BLE Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Protocol&amp;diff=2064"/>
		<updated>2026-06-24T10:56:28Z</updated>

		<summary type="html">&lt;p&gt;Admin: Initial Release [release]&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LLS Protocol is used in LIGO RS232 / RS485 fuel level sensor with the following parameters:&lt;br /&gt;
&lt;br /&gt;
* Baud rate: 2400,4800,9600,19200,38400,115200 (can be configured by software on PC)&lt;br /&gt;
* Data bits: 8&lt;br /&gt;
* Parity: None&lt;br /&gt;
* Stop bits: 1&lt;br /&gt;
* Flow control: None&lt;br /&gt;
&lt;br /&gt;
LIGO fuel level sensor has 2 working modes:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Slave mode:&#039;&#039;&#039; In this mode, the sensor will respond to all requests from the external device (master device). Each sensor in the network will be distinguished by different addresses configured with the LIGO Configurator software&lt;br /&gt;
# &#039;&#039;&#039;Master mode:&#039;&#039;&#039; In this mode the sensor will automatically send data to the external device with a preset interval (message interval)&lt;br /&gt;
&lt;br /&gt;
To enable this mode on the LIGO Configurator software, do the following:&lt;br /&gt;
&lt;br /&gt;
* Automatic transmission: select HEX (binary) or ASCII or ASCII EXT&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Automatic-Transmission.png|frameless|219px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Automatic transmission setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Set the time of each transfer (Message interval)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Message-Interval.png|frameless|228px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Message interval setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Exchange Protocol Description ==&lt;br /&gt;
&lt;br /&gt;
The LLS protocol supports two types of exchange protocols: binary (HEX) and character view (transmission of ASCII sequences). It is recommended to use binary exchange protocol.&lt;br /&gt;
&lt;br /&gt;
=== Format of Binary Protocol Messages ===&lt;br /&gt;
&lt;br /&gt;
All the commands of the binary communication protocol have the same standardized format which is given in the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequential number of the field !! Field name !! Field size, byte !! Description&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 1 || Prefix || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is a marker of the message beginning shall have prefix 31h, and an outcoming messages shall be displayed with 3Eh prefix by the program.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 2 || Network address || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the network address of the recipient. &amp;lt;br&amp;gt;For prefix 3Eh specify the network address of the sender.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 3 || Operation code || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the code of operation which the program shall perform For Eh prefix specify the code of operation to which the response is given&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 4 || Data || style=&amp;quot;text-align:center;&amp;quot; | It depends on the operation code || Data composition and format of the field depends on the operation code&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 5 || Checksum || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is used to control over data integrity&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Single-Stage Data Reading (command 06h) ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The data are transmitted with a lower byte ahead.&lt;br /&gt;
&lt;br /&gt;
==== Command format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output (command 07h) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to switch on periodic data output.&lt;br /&gt;
&lt;br /&gt;
After the command is processed, the sensor starts sending data periodically — level, temperature, and frequency — with the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power power supply (if the data output mode is not istalled by default).&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Periodic data output format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output Interval Adjustment (13h command) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to set up interval of periodic data output.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 0…255 || style=&amp;quot;text-align:left;&amp;quot; | Interval of the data output in seconds&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Default Data Output Mode (command 17h) ===&lt;br /&gt;
&lt;br /&gt;
This command determines the order of data output after the sensor is powered on or the processor is reset. After the power is on or the processor is reset, the program will send data periodically via the interface at the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | +3 || rowspan=&amp;quot;4&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in binary form&lt;br /&gt;
|-&lt;br /&gt;
| 02h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII mode)&lt;br /&gt;
|-&lt;br /&gt;
| 03h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII EXT mode)&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h…FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Description of Commands for the Text-Based Protocol ==&lt;br /&gt;
&lt;br /&gt;
Data exchange via the text-based protocol includes receipt and sending of ASCII symbols sequence interpreted and the request and response commands.&lt;br /&gt;
&lt;br /&gt;
=== Reading the Data ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The command is a sequence of symbols ASCII &amp;quot;D&amp;quot; and &amp;quot;O&amp;quot;. After receipt of the &amp;quot;DO&amp;quot; command the program will response in the form of ASCII symbols sequence.&lt;br /&gt;
&lt;br /&gt;
For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. All values are in hexadecimal form.&lt;br /&gt;
&lt;br /&gt;
In case the frequency value exceeds FFFh, the data are considered invalid.&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output ===&lt;br /&gt;
&lt;br /&gt;
The command is designed to switch on periodic data output. After processing the command, the sensor performs periodic data output in the text-based form (ASCII codes) of the following data: relative level, temperature, frequency.&lt;br /&gt;
&lt;br /&gt;
The data are being output periodically with an interval set up when cofiguring the sensor (LIGO Configurator software). In case the data output interval is set to zero, the data output won&#039;t be performed.&lt;br /&gt;
&lt;br /&gt;
Switching on of the periodic data output is done by sending of the &amp;quot;DP&amp;quot; symbols in line. After processing of the command, the symbols line will be received. For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power supply.&lt;br /&gt;
&lt;br /&gt;
== Checksum Calculation Algorithm ==&lt;br /&gt;
&lt;br /&gt;
The checksum is calculated using Dallas APPLICATION NOTE 27 table method: &#039;&#039;Understanding and Using Cyclic Redundancy Checks with Dallas Semiconductor iButton Products&#039;&#039;. One can use the following algorithms to calculate the checksum with a polynom &amp;lt;code&amp;gt;x^8 + x^5 + x^4 + 1&amp;lt;/code&amp;gt; (C language):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
U8 CRC8(U8 data, U8 crc)&lt;br /&gt;
{&lt;br /&gt;
    U8 i = data ^ crc;&lt;br /&gt;
    crc = 0;&lt;br /&gt;
    if (i &amp;amp; 0x01) crc ^= 0x5e;&lt;br /&gt;
    if (i &amp;amp; 0x02) crc ^= 0xbc;&lt;br /&gt;
    if (i &amp;amp; 0x04) crc ^= 0x61;&lt;br /&gt;
    if (i &amp;amp; 0x08) crc ^= 0xc2;&lt;br /&gt;
    if (i &amp;amp; 0x10) crc ^= 0x9d;&lt;br /&gt;
    if (i &amp;amp; 0x20) crc ^= 0x23;&lt;br /&gt;
    if (i &amp;amp; 0x40) crc ^= 0x46;&lt;br /&gt;
    if (i &amp;amp; 0x80) crc ^= 0x8c;&lt;br /&gt;
    return crc;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO BLE ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_BLE_Protocol&amp;diff=2063</id>
		<title>Category:LIGO PRO BLE Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_BLE_Protocol&amp;diff=2063"/>
		<updated>2026-06-24T10:56:18Z</updated>

		<summary type="html">&lt;p&gt;Admin: Auto-created category for LIGO PRO BLE Protocol&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- auto-created category --&amp;gt;&lt;br /&gt;
{{DEFAULTSORT:05 LIGO PRO BLE Protocol}}&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO BLE]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Protocol&amp;diff=2062</id>
		<title>LIGO PRO BLE Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_BLE_Protocol&amp;diff=2062"/>
		<updated>2026-06-24T10:56:18Z</updated>

		<summary type="html">&lt;p&gt;Admin: Create page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brief introduction to the protocol per IEC/IEEE standards for wireless data protocols.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Protocol name and version&#039;&#039;&#039; — full name + version number (vd: SOJI Protocol v2.1)&lt;br /&gt;
* &#039;&#039;&#039;Purpose&#039;&#039;&#039; — what data the protocol transmits and to whom&lt;br /&gt;
* &#039;&#039;&#039;Transport layer&#039;&#039;&#039; — Bluetooth 5.4 BLE / WiFi / LoRa / proprietary RF&lt;br /&gt;
* &#039;&#039;&#039;Frequency &amp;amp; modulation&#039;&#039;&#039; — 2.4 GHz GFSK / LoRa CSS / ...&lt;br /&gt;
* &#039;&#039;&#039;Operating mode&#039;&#039;&#039; — Advertising-only (one-way broadcast) / Connection-based / Mesh&lt;br /&gt;
* &#039;&#039;&#039;Compatible devices&#039;&#039;&#039; — list of sensors / receivers supporting this protocol&lt;br /&gt;
* &#039;&#039;&#039;Reference standards&#039;&#039;&#039; — IEEE 802.15.4, Bluetooth Core 5.4, etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example structure:&#039;&#039;&#039;&lt;br /&gt;
* Paragraph 1: &amp;quot;The [Protocol Name] is a [type] protocol defined by SOJI Electronics for [purpose]...&amp;quot;&lt;br /&gt;
* Paragraph 2: &amp;quot;It operates over [transport] at [frequency] and is supported by [device list]...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Optional sub-sections:&#039;&#039;&#039;&lt;br /&gt;
* === Protocol Stack === — layered diagram of protocol stack&lt;br /&gt;
* === Roles === — Broadcaster / Observer / Central / Peripheral&lt;br /&gt;
* === Security === — encryption, authentication, password protection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advertising Message Format ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Byte-by-byte breakdown of the BLE advertising packet payload.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Packet overview&#039;&#039;&#039; — total packet length, manufacturer ID, structure summary&lt;br /&gt;
* &#039;&#039;&#039;Field table&#039;&#039;&#039; — every field with offset, length, value, description&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example field breakdown table:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Length (bytes) !! Field Name !! Value / Format !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || 1 || AD Length || 0xXX || Length of the advertising data structure&lt;br /&gt;
|-&lt;br /&gt;
| 0x01 || 1 || AD Type || 0xFF || Manufacturer Specific Data (per Bluetooth Core spec)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02 || 2 || Company ID || 0xXXXX (LE) || SOJI Electronics SIG-assigned Company ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x04 || 1 || Protocol ID || 0xXX || SOJI Protocol identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x05 || 1 || Frame Type || 0xXX || Frame type (data frame, status frame, ...)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06 || N || Payload || ... || Sensor data payload (see Payload Structure below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06+N || 2 || CRC-16 || 0xXXXX || CRC-16/CCITT checksum over Offset 0x00 to 0x05+N&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sub-sections to consider:&#039;&#039;&#039;&lt;br /&gt;
* === Packet Overview === — total length, byte order (LE/BE), encoding&lt;br /&gt;
* === Field Definitions === — main field table above&lt;br /&gt;
* === Payload Structure === — detailed payload fields (level, temperature, battery, RSSI, etc.) with units and scaling&lt;br /&gt;
* === Frame Types === — enumeration of frame types if multiple exist&lt;br /&gt;
* === CRC / Checksum === — algorithm (CRC-16/CCITT, polynomial, init value, XOR-out)&lt;br /&gt;
* === Example Raw Frame === — hex dump of a real advertising packet with field annotations&lt;br /&gt;
* === Decoding Pseudocode === — short code snippet (C / Python) showing how to parse a frame&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example raw frame block:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Raw bytes (hex):&lt;br /&gt;
  0E FF 5A A5 01 02 03 04 ...&lt;br /&gt;
&lt;br /&gt;
Decoded:&lt;br /&gt;
  AD Length    = 0x0E (14 bytes)&lt;br /&gt;
  AD Type      = 0xFF (Manufacturer Specific)&lt;br /&gt;
  Company ID   = 0xA55A (SOJI Electronics)&lt;br /&gt;
  Protocol ID  = 0x01 (SOJI Protocol)&lt;br /&gt;
  Frame Type   = 0x02 (Data Frame)&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example decoding pseudocode:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def decode_soji_frame(adv_data: bytes) -&amp;gt; dict:&lt;br /&gt;
    if adv_data[1] != 0xFF:&lt;br /&gt;
        return None&lt;br /&gt;
    company_id = int.from_bytes(adv_data[2:4], &#039;little&#039;)&lt;br /&gt;
    protocol_id = adv_data[4]&lt;br /&gt;
    frame_type = adv_data[5]&lt;br /&gt;
    payload = adv_data[6:-2]&lt;br /&gt;
    crc = int.from_bytes(adv_data[-2:], &#039;little&#039;)&lt;br /&gt;
    # ... validate CRC, parse payload&lt;br /&gt;
    return { ... }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO BLE ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2061</id>
		<title>LIGO AIR Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2061"/>
		<updated>2026-06-24T10:55:44Z</updated>

		<summary type="html">&lt;p&gt;Admin: Initial Release [release]&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LIGO AIR Protocol utilizes &#039;&#039;&#039;Bluetooth Low Energy (BLE)&#039;&#039;&#039; to transmit structured fuel sensor data in advertising packets at regular intervals. Data includes fuel level, battery voltage of sensor, temperature, etc. For long-range applications, &#039;&#039;&#039;LIGO AIR Adapter&#039;&#039;&#039; is recommended to boost reception.&lt;br /&gt;
&lt;br /&gt;
In addition to its native protocol, LIGO AIR supports both the &#039;&#039;&#039;Escort&#039;&#039;&#039; and &#039;&#039;&#039;Mielta&#039;&#039;&#039; protocols, enabling seamless protocol switching without requiring additional integration effort. Users can configure the desired protocol via the mobile application.&lt;br /&gt;
&lt;br /&gt;
== Advertising Message Format ==&lt;br /&gt;
&lt;br /&gt;
Structure of &#039;&#039;&#039;Manufacturer Data&#039;&#039;&#039; field in advertising message (same for any type of peripheral unit).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example Manufacturer Data:&#039;&#039;&#039; &amp;lt;code&amp;gt;0x0CFFFC0D024D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequence number of bytes !! Description !! Example&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Length of field || &amp;lt;code&amp;gt;0x0C&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +1 || Type of field (Manufacturer data) || Always &amp;lt;code&amp;gt;0xFF&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +2 || Company ID || &amp;lt;code&amp;gt;0x0DFC&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| +4 || Hardware ID (type of device) || &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; — LIGO Air |&lt;br /&gt;
|-&lt;br /&gt;
| +5 and further || Payload (sensor parameters) || &amp;lt;code&amp;gt;0x4D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
In order to distinguish between types of hardware, the &#039;&#039;&#039;Hardware ID&#039;&#039;&#039; byte is used (see list of hardware below). The Hardware ID byte defines a set of useful parameters from the peripheral unit, their size, and order.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Byte of Hardware ID !! Type of Hardware&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt; || LIGO Air&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt; || LIGO Air Pro&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;The list of hardware may be expanded.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Payload data format of LIGO AIR (Hardware ID &amp;lt;code&amp;gt;0x02&amp;lt;/code&amp;gt;) and LIGO AIR Pro (Hardware ID &amp;lt;code&amp;gt;0x03&amp;lt;/code&amp;gt;).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
LIGO Air and LIGO Air Pro have the same payload data format. The structure of the payload is listed in the table below:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequence number of bytes !! Parameter type !! Parameter length (bytes) !! Range !! Unit of measurement&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Fuel level || 2 || 0 to 1023 (option), &amp;lt;br&amp;gt;0 to 4095 (default), &amp;lt;br&amp;gt;or 0 to 65535 (option, liters) || Arbitrary units&lt;br /&gt;
|-&lt;br /&gt;
| +2 || Reserved || 1 || N/A || N/A&lt;br /&gt;
|-&lt;br /&gt;
| +3 || Built-in battery voltage || 1 || 0 to 255 || Volt × 10&lt;br /&gt;
|-&lt;br /&gt;
| +4 || Temperature || 1 || −128 to 127 || Celsius degrees&lt;br /&gt;
|-&lt;br /&gt;
| +5 || Reserved || 1 || N/A || N/A&lt;br /&gt;
|-&lt;br /&gt;
| +6 || Firmware version || 1 || 0 to 255 || Version number&lt;br /&gt;
|-&lt;br /&gt;
| +7 || Operation Mode || 1 || 0 and 1 || 0: Working Mode &amp;lt;br&amp;gt;1: Storage Mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example Payload data:&#039;&#039;&#039; &amp;lt;code&amp;gt;0x4D0100241F000600&amp;lt;/code&amp;gt;&lt;br /&gt;
{| style=&amp;quot;border-collapse:collapse;margin:0 auto;text-align:center;font-family:Arial,sans-serif;font-size:11pt;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
| colspan=&amp;quot;7&amp;quot; style=&amp;quot;border:none;background:transparent;padding:0 0 6px 0;font-size:50pt;font-family:Consolas,&#039;Courier New&#039;,monospace;font-weight:bold;letter-spacing:1px;text-align:center;&amp;quot; | &amp;lt;span style=&amp;quot;color:#222&amp;quot;&amp;gt;0x&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#5E7440&amp;quot;&amp;gt;4D01&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#C99D3F&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#3170A8&amp;quot;&amp;gt;24&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#1F2D52&amp;quot;&amp;gt;1F&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#6E408A&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#1A1A1A&amp;quot;&amp;gt;06&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;color:#7BC571&amp;quot;&amp;gt;00&amp;lt;/span&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
! style=&amp;quot;background:#5E7440;color:#fff;padding:10px 18px;font-weight:bold;min-width:80px;&amp;quot; | 4D01&lt;br /&gt;
! style=&amp;quot;background:#C99D3F;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | NA&lt;br /&gt;
! style=&amp;quot;background:#3170A8;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 24&lt;br /&gt;
! style=&amp;quot;background:#1F2D52;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 1F&lt;br /&gt;
! style=&amp;quot;background:#6E408A;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | NA&lt;br /&gt;
! style=&amp;quot;background:#1A1A1A;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 06&lt;br /&gt;
! style=&amp;quot;background:#7BC571;color:#fff;padding:10px 18px;font-weight:bold;min-width:50px;&amp;quot; | 00&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;border:none;background:transparent;&amp;quot; |&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Fuel level&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Reserved&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Battery voltage&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Temperature&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Reserved&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Firmware version&lt;br /&gt;
| style=&amp;quot;border:1px solid #d0d0d0;padding:8px 12px;background:#fafafa;&amp;quot; | Operation Mode&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to little-endian (hex)&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 014D&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 24&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 1F&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 06&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 00&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to decimal&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 333&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 36&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 31&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 006&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:right;padding:6px 14px 6px 0;font-style:italic;color:#333;border:none;background:transparent;white-space:nowrap;&amp;quot; | Convert to value&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 333 (unit or liter)&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 3.6 Volt&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 31 ºC&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | NA&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | 0.0.6&lt;br /&gt;
| style=&amp;quot;padding:6px 12px;&amp;quot; | Working mode&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=Category:LIGO_AIR_Protocol&amp;diff=2060</id>
		<title>Category:LIGO AIR Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=Category:LIGO_AIR_Protocol&amp;diff=2060"/>
		<updated>2026-06-24T10:55:35Z</updated>

		<summary type="html">&lt;p&gt;Admin: Auto-created category for LIGO AIR Protocol&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- auto-created category --&amp;gt;&lt;br /&gt;
{{DEFAULTSORT:05 LIGO AIR Protocol}}&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2059</id>
		<title>LIGO AIR Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_AIR_Protocol&amp;diff=2059"/>
		<updated>2026-06-24T10:55:35Z</updated>

		<summary type="html">&lt;p&gt;Admin: Create page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brief introduction to the protocol per IEC/IEEE standards for wireless data protocols.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Protocol name and version&#039;&#039;&#039; — full name + version number (vd: SOJI Protocol v2.1)&lt;br /&gt;
* &#039;&#039;&#039;Purpose&#039;&#039;&#039; — what data the protocol transmits and to whom&lt;br /&gt;
* &#039;&#039;&#039;Transport layer&#039;&#039;&#039; — Bluetooth 5.4 BLE / WiFi / LoRa / proprietary RF&lt;br /&gt;
* &#039;&#039;&#039;Frequency &amp;amp; modulation&#039;&#039;&#039; — 2.4 GHz GFSK / LoRa CSS / ...&lt;br /&gt;
* &#039;&#039;&#039;Operating mode&#039;&#039;&#039; — Advertising-only (one-way broadcast) / Connection-based / Mesh&lt;br /&gt;
* &#039;&#039;&#039;Compatible devices&#039;&#039;&#039; — list of sensors / receivers supporting this protocol&lt;br /&gt;
* &#039;&#039;&#039;Reference standards&#039;&#039;&#039; — IEEE 802.15.4, Bluetooth Core 5.4, etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example structure:&#039;&#039;&#039;&lt;br /&gt;
* Paragraph 1: &amp;quot;The [Protocol Name] is a [type] protocol defined by SOJI Electronics for [purpose]...&amp;quot;&lt;br /&gt;
* Paragraph 2: &amp;quot;It operates over [transport] at [frequency] and is supported by [device list]...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Optional sub-sections:&#039;&#039;&#039;&lt;br /&gt;
* === Protocol Stack === — layered diagram of protocol stack&lt;br /&gt;
* === Roles === — Broadcaster / Observer / Central / Peripheral&lt;br /&gt;
* === Security === — encryption, authentication, password protection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advertising Message Format ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Byte-by-byte breakdown of the BLE advertising packet payload.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Packet overview&#039;&#039;&#039; — total packet length, manufacturer ID, structure summary&lt;br /&gt;
* &#039;&#039;&#039;Field table&#039;&#039;&#039; — every field with offset, length, value, description&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example field breakdown table:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Length (bytes) !! Field Name !! Value / Format !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || 1 || AD Length || 0xXX || Length of the advertising data structure&lt;br /&gt;
|-&lt;br /&gt;
| 0x01 || 1 || AD Type || 0xFF || Manufacturer Specific Data (per Bluetooth Core spec)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02 || 2 || Company ID || 0xXXXX (LE) || SOJI Electronics SIG-assigned Company ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x04 || 1 || Protocol ID || 0xXX || SOJI Protocol identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x05 || 1 || Frame Type || 0xXX || Frame type (data frame, status frame, ...)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06 || N || Payload || ... || Sensor data payload (see Payload Structure below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06+N || 2 || CRC-16 || 0xXXXX || CRC-16/CCITT checksum over Offset 0x00 to 0x05+N&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sub-sections to consider:&#039;&#039;&#039;&lt;br /&gt;
* === Packet Overview === — total length, byte order (LE/BE), encoding&lt;br /&gt;
* === Field Definitions === — main field table above&lt;br /&gt;
* === Payload Structure === — detailed payload fields (level, temperature, battery, RSSI, etc.) with units and scaling&lt;br /&gt;
* === Frame Types === — enumeration of frame types if multiple exist&lt;br /&gt;
* === CRC / Checksum === — algorithm (CRC-16/CCITT, polynomial, init value, XOR-out)&lt;br /&gt;
* === Example Raw Frame === — hex dump of a real advertising packet with field annotations&lt;br /&gt;
* === Decoding Pseudocode === — short code snippet (C / Python) showing how to parse a frame&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example raw frame block:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Raw bytes (hex):&lt;br /&gt;
  0E FF 5A A5 01 02 03 04 ...&lt;br /&gt;
&lt;br /&gt;
Decoded:&lt;br /&gt;
  AD Length    = 0x0E (14 bytes)&lt;br /&gt;
  AD Type      = 0xFF (Manufacturer Specific)&lt;br /&gt;
  Company ID   = 0xA55A (SOJI Electronics)&lt;br /&gt;
  Protocol ID  = 0x01 (SOJI Protocol)&lt;br /&gt;
  Frame Type   = 0x02 (Data Frame)&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example decoding pseudocode:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def decode_soji_frame(adv_data: bytes) -&amp;gt; dict:&lt;br /&gt;
    if adv_data[1] != 0xFF:&lt;br /&gt;
        return None&lt;br /&gt;
    company_id = int.from_bytes(adv_data[2:4], &#039;little&#039;)&lt;br /&gt;
    protocol_id = adv_data[4]&lt;br /&gt;
    frame_type = adv_data[5]&lt;br /&gt;
    payload = adv_data[6:-2]&lt;br /&gt;
    crc = int.from_bytes(adv_data[-2:], &#039;little&#039;)&lt;br /&gt;
    # ... validate CRC, parse payload&lt;br /&gt;
    return { ... }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO AIR ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=MediaWiki:Common.js&amp;diff=2058</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=MediaWiki:Common.js&amp;diff=2058"/>
		<updated>2026-06-24T10:55:05Z</updated>

		<summary type="html">&lt;p&gt;Admin: Update&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;(function () {&lt;br /&gt;
    function esc(text) {&lt;br /&gt;
        return String(text || &amp;quot;&amp;quot;).replace(/[&amp;amp;&amp;lt;&amp;gt;&amp;quot;]/g, function (m) {&lt;br /&gt;
            return { &amp;quot;&amp;amp;&amp;quot;: &amp;quot;&amp;amp;amp;&amp;quot;, &amp;quot;&amp;lt;&amp;quot;: &amp;quot;&amp;amp;lt;&amp;quot;, &amp;quot;&amp;gt;&amp;quot;: &amp;quot;&amp;amp;gt;&amp;quot;, &#039;&amp;quot;&#039;: &amp;quot;&amp;amp;quot;&amp;quot; }[m];&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var ICONS = {&lt;br /&gt;
        location: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 010-5 2.5 2.5 0 010 5z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        phone: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M12 1a9 9 0 00-9 9v7a3 3 0 003 3h3v-8H5v-2a7 7 0 0114 0v2h-4v8h3a3 3 0 003-3v-7a9 9 0 00-9-9z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        email: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;none&amp;quot; stroke=&amp;quot;currentColor&amp;quot; stroke-width=&amp;quot;2&amp;quot; stroke-linecap=&amp;quot;round&amp;quot; stroke-linejoin=&amp;quot;round&amp;quot;&amp;gt;&amp;lt;circle cx=&amp;quot;12&amp;quot; cy=&amp;quot;12&amp;quot; r=&amp;quot;4&amp;quot;/&amp;gt;&amp;lt;path d=&amp;quot;M16 8v5a3 3 0 006 0v-1a10 10 0 10-4 8&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        clock: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;none&amp;quot; stroke=&amp;quot;currentColor&amp;quot; stroke-width=&amp;quot;2&amp;quot; stroke-linecap=&amp;quot;round&amp;quot; stroke-linejoin=&amp;quot;round&amp;quot;&amp;gt;&amp;lt;circle cx=&amp;quot;12&amp;quot; cy=&amp;quot;12&amp;quot; r=&amp;quot;10&amp;quot;/&amp;gt;&amp;lt;polyline points=&amp;quot;12 6 12 12 16 14&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        facebook: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M22 12a10 10 0 10-11.56 9.88v-6.99H7.9V12h2.54V9.8c0-2.51 1.49-3.89 3.77-3.89 1.09 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56V12h2.77l-.44 2.89h-2.33v6.99A10 10 0 0022 12z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        whatsapp: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M17.5 14.4c-.3-.1-1.7-.8-1.9-.9-.3-.1-.5-.1-.7.1-.2.3-.7.9-.9 1.1-.2.2-.3.2-.6.1-.3-.1-1.2-.4-2.3-1.4-.9-.8-1.4-1.7-1.6-2-.2-.3 0-.5.1-.6.1-.1.3-.3.4-.5.1-.2.2-.3.3-.5.1-.2 0-.4 0-.5 0-.1-.7-1.7-1-2.3-.3-.6-.5-.5-.7-.5h-.6c-.2 0-.5.1-.8.4-.3.3-1 1-1 2.5s1.1 2.9 1.2 3.1c.1.2 2.1 3.2 5.1 4.5.7.3 1.3.5 1.7.6.7.2 1.4.2 1.9.1.6-.1 1.7-.7 2-1.4.2-.7.2-1.3.2-1.4-.1-.1-.3-.2-.5-.3zM12 2a10 10 0 00-8.5 15.3L2 22l4.8-1.4A10 10 0 1012 2z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        linkedin: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M19 3H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2zM8.3 18.3H5.7V9.7h2.6v8.6zM7 8.5a1.5 1.5 0 110-3 1.5 1.5 0 010 3zm11.3 9.8h-2.6v-4.2c0-1 0-2.3-1.4-2.3s-1.6 1.1-1.6 2.2v4.3h-2.6V9.7h2.5v1.2a2.7 2.7 0 012.5-1.4c2.6 0 3.1 1.7 3.1 4v4.8z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        youtube: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M23.5 6.2a3 3 0 00-2.1-2.1C19.5 3.5 12 3.5 12 3.5s-7.5 0-9.4.6A3 3 0 00.5 6.2 31.3 31.3 0 000 12a31.3 31.3 0 00.5 5.8 3 3 0 002.1 2.1c1.9.6 9.4.6 9.4.6s7.5 0 9.4-.6a3 3 0 002.1-2.1A31.3 31.3 0 0024 12a31.3 31.3 0 00-.5-5.8zM9.6 15.6V8.4l6.3 3.6-6.3 3.6z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    function getIcon(name) {&lt;br /&gt;
        return &#039;&amp;lt;span class=&amp;quot;soji-footer-icon&amp;quot;&amp;gt;&#039; + (ICONS[name] || &amp;quot;&amp;quot;) + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function renderLink(item, extraClass) {&lt;br /&gt;
    var cls = extraClass ? &#039; class=&amp;quot;&#039; + extraClass + &#039;&amp;quot;&#039; : &amp;quot;&amp;quot;;&lt;br /&gt;
    var href = item.href ? esc(item.href) : &amp;quot;#&amp;quot;;&lt;br /&gt;
    return &#039;&amp;lt;a&#039; + cls + &#039; href=&amp;quot;&#039; + href + &#039;&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;&#039; + esc(item.label) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    function buildCompany(company) {&lt;br /&gt;
        if (!company) return &amp;quot;&amp;quot;;&lt;br /&gt;
        var contacts = (company.contacts || []).map(function (c) {&lt;br /&gt;
            return &#039;&amp;lt;li class=&amp;quot;soji-footer-contact&amp;quot;&amp;gt;&#039; + getIcon(c.icon) +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + esc(c.text) + &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
        }).join(&amp;quot;&amp;quot;);&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;&amp;lt;section class=&amp;quot;soji-footer-company&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;h2 class=&amp;quot;soji-footer-company-name&amp;quot;&amp;gt;&#039; + esc(company.name) + &amp;quot;&amp;lt;/h2&amp;gt;&amp;quot;,&lt;br /&gt;
            &#039;&amp;lt;ul class=&amp;quot;soji-footer-contact-list&amp;quot;&amp;gt;&#039; + contacts + &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
        ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildColumns(columns) {&lt;br /&gt;
        return (columns || []).map(function (col) {&lt;br /&gt;
            var items = (col.items || []).map(function (item) {&lt;br /&gt;
                return &amp;quot;&amp;lt;li&amp;gt;&amp;quot; + renderLink(item, &amp;quot;soji-footer-link&amp;quot;) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
            }).join(&amp;quot;&amp;quot;);&lt;br /&gt;
            return [&lt;br /&gt;
                &#039;&amp;lt;section class=&amp;quot;soji-footer-col&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
                &amp;quot;&amp;lt;h2&amp;gt;&amp;quot; + esc(col.title) + &amp;quot;&amp;lt;/h2&amp;gt;&amp;quot;,&lt;br /&gt;
                &amp;quot;&amp;lt;ul&amp;gt;&amp;quot; + items + &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;,&lt;br /&gt;
                &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
            ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
        }).join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildSubscribe(data) {&lt;br /&gt;
        var sub = data.subscribe || {};&lt;br /&gt;
        var social = (data.social || []).map(function (item) {&lt;br /&gt;
    var href = item.href ? esc(item.href) : &amp;quot;#&amp;quot;;&lt;br /&gt;
    var icon = item.icon&lt;br /&gt;
        ? getIcon(item.icon)&lt;br /&gt;
        : esc(item.short || item.label.charAt(0));&lt;br /&gt;
    return &#039;&amp;lt;a class=&amp;quot;soji-footer-social-item&amp;quot; href=&amp;quot;&#039; + href + &#039;&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot; aria-label=&amp;quot;&#039; + esc(item.label) + &#039;&amp;quot;&amp;gt;&#039; + icon + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
}).join(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;&amp;lt;section class=&amp;quot;soji-footer-subscribe-col&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &amp;quot;&amp;lt;h2&amp;gt;&amp;quot; + esc(sub.title || &amp;quot;Subscribe&amp;quot;) + &amp;quot;&amp;lt;/h2&amp;gt;&amp;quot;,&lt;br /&gt;
            &#039;&amp;lt;form class=&amp;quot;soji-footer-subscribe-form&amp;quot; action=&amp;quot;&#039; + esc(sub.action || &amp;quot;#&amp;quot;) + &#039;&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;input type=&amp;quot;email&amp;quot; name=&amp;quot;email&amp;quot; class=&amp;quot;soji-footer-subscribe-input&amp;quot; placeholder=&amp;quot;&#039; + esc(sub.placeholder || &amp;quot;Email Address *&amp;quot;) + &#039;&amp;quot; required&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;submit&amp;quot; class=&amp;quot;soji-footer-subscribe-btn&amp;quot;&amp;gt;&#039; + esc(sub.buttonLabel || &amp;quot;Send&amp;quot;) + &amp;quot;&amp;lt;/button&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;,&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;soji-footer-social&amp;quot;&amp;gt;&#039; + social + &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
        ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildFooter(data) {&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;&amp;lt;section id=&amp;quot;soji-site-footer&amp;quot; class=&amp;quot;soji-site-footer&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;soji-footer-shell&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;soji-footer-grid&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            buildCompany(data.company),&lt;br /&gt;
            buildColumns(data.columns),&lt;br /&gt;
            buildSubscribe(data),&lt;br /&gt;
            &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
            data.copyright ? &#039;&amp;lt;div class=&amp;quot;soji-footer-copy&amp;quot;&amp;gt;&#039; + esc(data.copyright).replace(/\{\{YEAR\}\}/g, new Date().getFullYear()) + &amp;quot;&amp;lt;/div&amp;gt;&amp;quot; : &amp;quot;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
        ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function injectFooter(data) {&lt;br /&gt;
    if (document.getElementById(&amp;quot;soji-site-footer&amp;quot;)) return;&lt;br /&gt;
    document.body.insertAdjacentHTML(&amp;quot;beforeend&amp;quot;, buildFooter(data));&lt;br /&gt;
    document.body.classList.add(&amp;quot;has-soji-footer&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        fetch(&amp;quot;/index.php?title=MediaWiki:FooterData.json&amp;amp;action=raw&amp;quot;)&lt;br /&gt;
            .then(function (r) { return r.text(); })&lt;br /&gt;
            .then(function (text) { return JSON.parse(text); })&lt;br /&gt;
            .then(injectFooter)&lt;br /&gt;
            .catch(function (err) { console.error(&amp;quot;Footer load failed:&amp;quot;, err); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &amp;quot;loading&amp;quot;) {&lt;br /&gt;
        document.addEventListener(&amp;quot;DOMContentLoaded&amp;quot;, init);&lt;br /&gt;
    } else {&lt;br /&gt;
        init();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    if (mw.config.get(&#039;wgNamespaceNumber&#039;) &amp;lt; 0) return;&lt;br /&gt;
&lt;br /&gt;
    var title = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
    var pageName = mw.config.get(&#039;wgPageName&#039;).replace(/_/g, &#039; &#039;);&lt;br /&gt;
&lt;br /&gt;
    // Page Main Page: không cần breadcrumb&lt;br /&gt;
    if (pageName === &#039;Main Page&#039;) return;&lt;br /&gt;
&lt;br /&gt;
    // Lấy category trực tiếp của page hoặc của category page hiện tại&lt;br /&gt;
    var initialCats = mw.config.get(&#039;wgCategories&#039;) || [];&lt;br /&gt;
    if (!initialCats.length) return;&lt;br /&gt;
&lt;br /&gt;
    var api = new mw.Api();&lt;br /&gt;
    var chain = [];&lt;br /&gt;
    var seen = {};&lt;br /&gt;
&lt;br /&gt;
    // Đi ngược lên cây qua API&lt;br /&gt;
    function walkUp(catName) {&lt;br /&gt;
        if (!catName || seen[catName] || catName === &#039;Main Page&#039;) {&lt;br /&gt;
            return $.Deferred().resolve();&lt;br /&gt;
        }&lt;br /&gt;
        seen[catName] = true;&lt;br /&gt;
        chain.unshift(catName);&lt;br /&gt;
&lt;br /&gt;
        return api.get({&lt;br /&gt;
            action: &#039;query&#039;,&lt;br /&gt;
            titles: &#039;Category:&#039; + catName,&lt;br /&gt;
            prop: &#039;categories&#039;,&lt;br /&gt;
            cllimit: 1,&lt;br /&gt;
            format: &#039;json&#039;,&lt;br /&gt;
            formatversion: 2&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            if (!pages.length || !pages[0].categories) return;&lt;br /&gt;
            var parent = pages[0].categories[0].title.replace(/^Category:/, &#039;&#039;);&lt;br /&gt;
            return walkUp(parent);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    walkUp(initialCats[0]).then(function () {&lt;br /&gt;
        var parts = [&lt;br /&gt;
            &#039;&amp;lt;a href=&amp;quot;&#039; + mw.util.getUrl(&#039;Main Page&#039;) + &#039;&amp;quot;&amp;gt;Main Page&amp;lt;/a&amp;gt;&#039;&lt;br /&gt;
        ];&lt;br /&gt;
        chain.forEach(function (c) {&lt;br /&gt;
            parts.push(&#039;&amp;lt;a href=&amp;quot;&#039; + mw.util.getUrl(c) + &#039;&amp;quot;&amp;gt;&#039; + c + &#039;&amp;lt;/a&amp;gt;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        parts.push(&#039;&amp;lt;strong&amp;gt;&#039; + title + &#039;&amp;lt;/strong&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var html = &#039;&amp;lt;nav class=&amp;quot;custom-breadcrumb&amp;quot;&amp;gt;&#039; + parts.join(&#039; &amp;amp;gt; &#039;) + &#039;&amp;lt;/nav&amp;gt;&#039;;&lt;br /&gt;
        $(&#039;#mw-content-text&#039;).before(html);&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Tự động thêm target=&amp;quot;_blank&amp;quot; cho mọi link tới file .pdf&lt;br /&gt;
    $(&#039;a[href$=&amp;quot;.pdf&amp;quot;], a[href*=&amp;quot;.pdf?&amp;quot;], a[href*=&amp;quot;/file/&amp;quot;][href*=&amp;quot;.pdf&amp;quot;]&#039;).each(function () {&lt;br /&gt;
        $(this).attr(&#039;target&#039;, &#039;_blank&#039;);&lt;br /&gt;
        $(this).attr(&#039;rel&#039;, &#039;noopener noreferrer&#039;);&lt;br /&gt;
        // Thêm icon PDF nhỏ&lt;br /&gt;
        if (!$(this).find(&#039;.pdf-icon&#039;).length) {&lt;br /&gt;
            $(this).append(&#039; &amp;lt;span class=&amp;quot;pdf-icon&amp;quot; title=&amp;quot;Open PDF in new tab&amp;quot;&amp;gt;📄&amp;lt;/span&amp;gt;&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Force &amp;quot;View&amp;quot; links to open in new tab&lt;br /&gt;
    $(&#039;.doc-view a&#039;).attr({&lt;br /&gt;
        target: &#039;_blank&#039;,&lt;br /&gt;
        rel: &#039;noopener noreferrer&#039;&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Force &amp;quot;Download&amp;quot; links to trigger file download&lt;br /&gt;
    $(&#039;.doc-download a&#039;).each(function () {&lt;br /&gt;
        var $a = $(this);&lt;br /&gt;
        var href = $a.attr(&#039;href&#039;);&lt;br /&gt;
        // Extract filename from URL&lt;br /&gt;
        var match = href.match(/\/([^\/]+\.(?:pdf|zip|doc|docx|xls|xlsx))$/i);&lt;br /&gt;
        if (match) {&lt;br /&gt;
            $a.attr(&#039;download&#039;, match[1]);&lt;br /&gt;
        } else {&lt;br /&gt;
            $a.attr(&#039;download&#039;, &#039;&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================&lt;br /&gt;
 * Auto-collapse TOC sub-headings on page load&lt;br /&gt;
 * Only show H2 (main Heading) by default&lt;br /&gt;
 * ============================================ */&lt;br /&gt;
$(function () {&lt;br /&gt;
    var attempts = 0;&lt;br /&gt;
    var maxAttempts = 15;&lt;br /&gt;
&lt;br /&gt;
    function collapseAllTOC() {&lt;br /&gt;
        // Find all expanded toggle buttons in TOC&lt;br /&gt;
        var $toggles = $(&#039;.vector-toc-toggle[aria-expanded=&amp;quot;true&amp;quot;]&#039;);&lt;br /&gt;
        &lt;br /&gt;
        if ($toggles.length &amp;gt; 0) {&lt;br /&gt;
            $toggles.each(function () {&lt;br /&gt;
                // Click toggle to collapse (triggers Vue.js handler)&lt;br /&gt;
                this.click();&lt;br /&gt;
            });&lt;br /&gt;
        } else if (attempts &amp;lt; maxAttempts) {&lt;br /&gt;
            // TOC chưa render xong, retry&lt;br /&gt;
            attempts++;&lt;br /&gt;
            setTimeout(collapseAllTOC, 100);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    collapseAllTOC();&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================&lt;br /&gt;
 * Sync chevron arrow with collapsible state&lt;br /&gt;
 * ============================================ */&lt;br /&gt;
$(function () {&lt;br /&gt;
    $(&#039;.category-block-toggle span[class*=&amp;quot;mw-customtoggle-cb-&amp;quot;]&#039;).each(function () {&lt;br /&gt;
        var $toggle = $(this);&lt;br /&gt;
        var match = $toggle.attr(&#039;class&#039;).match(/mw-customtoggle-(\S+)/);&lt;br /&gt;
        if (!match) return;&lt;br /&gt;
&lt;br /&gt;
        var $target = $(&#039;#mw-customcollapsible-&#039; + match[1]);&lt;br /&gt;
        if (!$target.length) return;&lt;br /&gt;
&lt;br /&gt;
        function syncArrow() {&lt;br /&gt;
            if ($target.hasClass(&#039;mw-collapsed&#039;)) {&lt;br /&gt;
                $toggle.removeClass(&#039;is-expanded&#039;);&lt;br /&gt;
            } else {&lt;br /&gt;
                $toggle.addClass(&#039;is-expanded&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Initial state&lt;br /&gt;
        syncArrow();&lt;br /&gt;
&lt;br /&gt;
        // Update on click&lt;br /&gt;
        $toggle.on(&#039;click&#039;, function () {&lt;br /&gt;
            setTimeout(syncArrow, 50);&lt;br /&gt;
        });&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================&lt;br /&gt;
 * Pending Changes Badge for Reviewers&lt;br /&gt;
 * Show notification of pending changes count&lt;br /&gt;
 * ============================================ */&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Chỉ show cho user có quyền review (reviewer, sysop, admin)&lt;br /&gt;
    var userGroups = mw.config.get(&#039;wgUserGroups&#039;) || [];&lt;br /&gt;
    var hasReviewRights = userGroups.some(function (g) {&lt;br /&gt;
        return [&#039;reviewer&#039;, &#039;sysop&#039;, &#039;bureaucrat&#039;].indexOf(g) !== -1;&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (!hasReviewRights) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetch pending count from API&lt;br /&gt;
    function fetchPendingCount() {&lt;br /&gt;
        new mw.Api().get({&lt;br /&gt;
            action: &#039;query&#039;,&lt;br /&gt;
            list: &#039;oldreviewedpages&#039;,&lt;br /&gt;
            ornamespace: &#039;0|14&#039;,&lt;br /&gt;
            orfilterredir: &#039;nonredirects&#039;,&lt;br /&gt;
            orlimit: 500,&lt;br /&gt;
            format: &#039;json&#039;&lt;br /&gt;
        }).done(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.oldreviewedpages) || [];&lt;br /&gt;
            updateBadge(pages.length);&lt;br /&gt;
        }).fail(function (err) {&lt;br /&gt;
            console.log(&#039;Failed to fetch pending changes:&#039;, err);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Update or create badge in sidebar&lt;br /&gt;
    function updateBadge(count) {&lt;br /&gt;
        var $badge = $(&#039;#pending-reviews-badge&#039;);&lt;br /&gt;
&lt;br /&gt;
        if (!$badge.length) {&lt;br /&gt;
            // Tạo badge mới — tìm sidebar và prepend&lt;br /&gt;
            var badgeHtml =&lt;br /&gt;
                &#039;&amp;lt;div id=&amp;quot;pending-reviews-badge&amp;quot; style=&amp;quot;&#039; +&lt;br /&gt;
                &#039;padding: 12px 16px;&#039; +&lt;br /&gt;
                &#039;margin: 12px 8px;&#039; +&lt;br /&gt;
                &#039;background: #fff3e0;&#039; +&lt;br /&gt;
                &#039;border: 1px solid #f57c00;&#039; +&lt;br /&gt;
                &#039;border-left: 4px solid #e65100;&#039; +&lt;br /&gt;
                &#039;border-radius: 4px;&#039; +&lt;br /&gt;
                &#039;cursor: pointer;&#039; +&lt;br /&gt;
                &#039;transition: background 0.2s;&#039; +&lt;br /&gt;
                &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;a href=&amp;quot;&#039; + mw.util.getUrl(&#039;Special:PendingChanges&#039;) + &#039;&amp;quot; style=&amp;quot;&#039; +&lt;br /&gt;
                &#039;color: #e65100;&#039; +&lt;br /&gt;
                &#039;text-decoration: none;&#039; +&lt;br /&gt;
                &#039;font-weight: 600;&#039; +&lt;br /&gt;
                &#039;font-size: 0.95em;&#039; +&lt;br /&gt;
                &#039;display: block;&#039; +&lt;br /&gt;
                &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;⚠️ &amp;lt;span id=&amp;quot;pending-count&amp;quot; style=&amp;quot;&#039; +&lt;br /&gt;
                &#039;display: inline-block;&#039; +&lt;br /&gt;
                &#039;background: #e65100;&#039; +&lt;br /&gt;
                &#039;color: white;&#039; +&lt;br /&gt;
                &#039;padding: 2px 10px;&#039; +&lt;br /&gt;
                &#039;border-radius: 12px;&#039; +&lt;br /&gt;
                &#039;margin: 0 6px;&#039; +&lt;br /&gt;
                &#039;font-weight: 700;&#039; +&lt;br /&gt;
                &#039;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;pending review&#039; +&lt;br /&gt;
                &#039;&amp;lt;/a&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
            // Try multiple sidebar selectors (Vector 2022 + legacy)&lt;br /&gt;
            var $sidebar = $(&#039;#mw-panel .vector-main-menu-content&#039;)&lt;br /&gt;
                .add(&#039;#mw-panel-toc-list&#039;)&lt;br /&gt;
                .add(&#039;#mw-panel&#039;)&lt;br /&gt;
                .add(&#039;.vector-main-menu&#039;)&lt;br /&gt;
                .first();&lt;br /&gt;
&lt;br /&gt;
            if ($sidebar.length) {&lt;br /&gt;
                $sidebar.prepend(badgeHtml);&lt;br /&gt;
                $badge = $(&#039;#pending-reviews-badge&#039;);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Fallback: insert at top of body&lt;br /&gt;
                $(&#039;body&#039;).prepend(&lt;br /&gt;
                    &#039;&amp;lt;div style=&amp;quot;position:fixed;top:60px;right:20px;z-index:9999;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                    badgeHtml + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                );&lt;br /&gt;
                $badge = $(&#039;#pending-reviews-badge&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $(&#039;#pending-count&#039;).text(count);&lt;br /&gt;
&lt;br /&gt;
        if (count === 0) {&lt;br /&gt;
            $badge.hide();&lt;br /&gt;
        } else {&lt;br /&gt;
            $badge.show();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Initial fetch&lt;br /&gt;
    fetchPendingCount();&lt;br /&gt;
&lt;br /&gt;
    // Refresh every 60 seconds&lt;br /&gt;
    setInterval(fetchPendingCount, 60000);&lt;br /&gt;
&lt;br /&gt;
    // Also refresh on focus (when user comes back to tab)&lt;br /&gt;
    $(window).on(&#039;focus&#039;, fetchPendingCount);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Placeholder auto-hide&lt;br /&gt;
 *  Logic: section nào có user content → ẩn placeholder trong section đó&lt;br /&gt;
 *         section trống chỉ có placeholder → vẫn hiển thị (làm hint)&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function hidePlaceholdersInFilledSections() {&lt;br /&gt;
        var $headings = $( &#039;.mw-parser-output h1, .mw-parser-output h2&#039; );&lt;br /&gt;
        if ( !$headings.length ) return;&lt;br /&gt;
&lt;br /&gt;
        $headings.each( function ( i ) {&lt;br /&gt;
            var $h = $( this );&lt;br /&gt;
            var $nextH = $headings.eq( i + 1 );&lt;br /&gt;
            var $sectionEls = $nextH.length ? $h.nextUntil( $nextH ) : $h.nextAll();&lt;br /&gt;
&lt;br /&gt;
            // Đọc text trong section, loại trừ text của placeholder&lt;br /&gt;
            var contentText = &#039;&#039;;&lt;br /&gt;
            $sectionEls.each( function () {&lt;br /&gt;
                var $clone = $( this ).clone();&lt;br /&gt;
                $clone.find( &#039;.soji-placeholder&#039; ).remove();&lt;br /&gt;
                contentText += $clone.text();&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            // Nếu section có user content (ngoài placeholder) → ẩn placeholder&lt;br /&gt;
            if ( contentText.trim().length &amp;gt; 0 ) {&lt;br /&gt;
                $sectionEls.find( &#039;.soji-placeholder&#039; ).hide();&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // Xử lý riêng placeholder trong table cell:&lt;br /&gt;
        // Nếu cell có content khác → ẩn placeholder của cell đó&lt;br /&gt;
        $( &#039;.soji-placeholder:visible&#039; ).each( function () {&lt;br /&gt;
            var $ph = $( this );&lt;br /&gt;
            var $cell = $ph.closest( &#039;td, th&#039; );&lt;br /&gt;
            if ( $cell.length ) {&lt;br /&gt;
                var $clone = $cell.clone();&lt;br /&gt;
                $clone.find( &#039;.soji-placeholder&#039; ).remove();&lt;br /&gt;
                if ( $clone.text().trim().length &amp;gt; 0 ) {&lt;br /&gt;
                    $ph.hide();&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( hidePlaceholdersInFilledSections );&lt;br /&gt;
&lt;br /&gt;
})( jQuery );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Edit summary helper&lt;br /&gt;
 *  Show hint placeholder in edit summary field&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    $( function () {&lt;br /&gt;
        if ( mw.config.get( &#039;wgAction&#039; ) !== &#039;edit&#039; &amp;amp;&amp;amp; mw.config.get( &#039;wgAction&#039; ) !== &#039;submit&#039; ) return;&lt;br /&gt;
&lt;br /&gt;
        var $summary = $( &#039;#wpSummary&#039; );&lt;br /&gt;
        if ( !$summary.length ) return;&lt;br /&gt;
&lt;br /&gt;
        // Add placeholder hint&lt;br /&gt;
        $summary.attr( &#039;placeholder&#039;,&lt;br /&gt;
            &#039;Describe your changes (will appear in Revision History — e.g., &amp;quot;Updated electrical specs&amp;quot;, &amp;quot;Fixed typo in safety section&amp;quot;)&#039;&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        // Add character counter&lt;br /&gt;
        var $counter = $( &#039;&amp;lt;div class=&amp;quot;fw-summary-counter&amp;quot; style=&amp;quot;font-size:0.85em; color:#666; margin-top:4px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039; );&lt;br /&gt;
        $summary.after( $counter );&lt;br /&gt;
&lt;br /&gt;
        function updateCounter() {&lt;br /&gt;
            var len = $summary.val().length;&lt;br /&gt;
            var color = len &amp;lt; 10 ? &#039;#d33&#039; : len &amp;lt; 30 ? &#039;#f80&#039; : &#039;#0a0&#039;;&lt;br /&gt;
            $counter.html(&lt;br /&gt;
                &#039;&amp;lt;span style=&amp;quot;color:&#039; + color + &#039;&amp;quot;&amp;gt;&#039; + len + &#039;&amp;lt;/span&amp;gt; characters &#039; +&lt;br /&gt;
                ( len &amp;lt; 10 ? &#039;(too short — describe what changed)&#039; :&lt;br /&gt;
                  len &amp;lt; 30 ? &#039;(acceptable — be more specific)&#039; :&lt;br /&gt;
                  &#039;(good — descriptive)&#039; )&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $summary.on( &#039;input keyup&#039;, updateCounter );&lt;br /&gt;
        updateCounter();&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Auto Revision History Table&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function formatVersion( n ) {&lt;br /&gt;
        var major = Math.floor( ( n - 1 ) / 10 ) + 1;&lt;br /&gt;
        var minor = ( n - 1 ) % 10;&lt;br /&gt;
        return major + &#039;.&#039; + minor;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function formatDate( isoTimestamp ) {&lt;br /&gt;
        var d = new Date( isoTimestamp );&lt;br /&gt;
        var dd = ( &#039;0&#039; + d.getDate() ).slice( -2 );&lt;br /&gt;
        var mm = ( &#039;0&#039; + ( d.getMonth() + 1 ) ).slice( -2 );&lt;br /&gt;
        return dd + &#039;/&#039; + mm + &#039;/&#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeHtml( str ) {&lt;br /&gt;
        return ( str || &#039;&#039; )&lt;br /&gt;
            .replace( /&amp;amp;/g, &#039;&amp;amp;amp;&#039; )&lt;br /&gt;
            .replace( /&amp;lt;/g, &#039;&amp;amp;lt;&#039; )&lt;br /&gt;
            .replace( /&amp;gt;/g, &#039;&amp;amp;gt;&#039; )&lt;br /&gt;
            .replace( /&amp;quot;/g, &#039;&amp;amp;quot;&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderRevisionHistory() {&lt;br /&gt;
    var $containers = $( &#039;.revision-history-auto&#039; );&lt;br /&gt;
    if ( !$containers.length ) return;&lt;br /&gt;
&lt;br /&gt;
    var pageName = mw.config.get( &#039;wgPageName&#039; );&lt;br /&gt;
&lt;br /&gt;
    new mw.Api().get( {&lt;br /&gt;
        action: &#039;query&#039;,&lt;br /&gt;
        prop: &#039;revisions&#039;,&lt;br /&gt;
        titles: pageName,&lt;br /&gt;
        rvprop: &#039;timestamp|user|comment&#039;,&lt;br /&gt;
        rvlimit: &#039;max&#039;,&lt;br /&gt;
        rvdir: &#039;newer&#039;,           // oldest first&lt;br /&gt;
        formatversion: 2&lt;br /&gt;
    } ).done( function ( data ) {&lt;br /&gt;
        var page = data.query.pages[ 0 ];&lt;br /&gt;
        if ( !page || !page.revisions ) {&lt;br /&gt;
            $containers.html( &#039;&amp;lt;em&amp;gt;No revision history available.&amp;lt;/em&amp;gt;&#039; );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var revisions = page.revisions;&lt;br /&gt;
&lt;br /&gt;
        // Case 1: chỉ có 1 revision = template gen, chưa edit&lt;br /&gt;
        if ( revisions.length &amp;lt; 2 ) {&lt;br /&gt;
            $containers.html(&lt;br /&gt;
                &#039;&amp;lt;em style=&amp;quot;color:#888&amp;quot;&amp;gt;No edits yet. &#039; +&lt;br /&gt;
                &#039;Edit this page to update the content — the first edit will be recorded as v1.0.&amp;lt;/em&amp;gt;&#039;&lt;br /&gt;
            );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Case 2: có &amp;gt;= 2 revisions&lt;br /&gt;
        // - revision 0 = template creation → SKIP&lt;br /&gt;
        // - revision 1 = first edit → v1.0&lt;br /&gt;
        // - revision 2 = second edit → v1.1&lt;br /&gt;
        // - ...&lt;br /&gt;
        var rows = [];&lt;br /&gt;
&lt;br /&gt;
        revisions.forEach( function ( rev, i ) {&lt;br /&gt;
            // Skip first revision (template creation)&lt;br /&gt;
            if ( i === 0 ) return;&lt;br /&gt;
&lt;br /&gt;
            // i=1 → v1.0, i=2 → v1.1, ...&lt;br /&gt;
            var version = formatVersion( i );&lt;br /&gt;
            var date    = formatDate( rev.timestamp );&lt;br /&gt;
            var author  = escapeHtml( rev.user || &#039;unknown&#039; );&lt;br /&gt;
            var desc    = escapeHtml( rev.comment ) || &#039;&amp;lt;em&amp;gt;(no edit summary)&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
            rows.push(&lt;br /&gt;
                &#039;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039; + date + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;td&amp;gt;&amp;lt;strong&amp;gt;v&#039; + version + &#039;&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;td&amp;gt;&#039; + author + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;td&amp;gt;&#039; + desc + &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;&lt;br /&gt;
            );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        // Newest first&lt;br /&gt;
        rows.reverse();&lt;br /&gt;
&lt;br /&gt;
        var html =&lt;br /&gt;
            &#039;&amp;lt;table class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Date&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Version&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th style=&amp;quot;width:20%&amp;quot;&amp;gt;Author&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;tbody&amp;gt;&#039; + rows.join( &#039;&#039; ) + &#039;&amp;lt;/tbody&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/table&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
        $containers.html( html );&lt;br /&gt;
    } ).fail( function ( err ) {&lt;br /&gt;
        $containers.html( &#039;&amp;lt;em style=&amp;quot;color:#d33&amp;quot;&amp;gt;Failed to load revision history.&amp;lt;/em&amp;gt;&#039; );&lt;br /&gt;
    } );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    $( renderRevisionHistory );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Force edit summary in VisualEditor save dialog&lt;br /&gt;
 *  Disable Save button until summary is non-empty (min 5 chars)&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var MIN_LENGTH = 5;&lt;br /&gt;
&lt;br /&gt;
    function setupVEEnforcement() {&lt;br /&gt;
        // Wait for VE save dialog to appear&lt;br /&gt;
        var observer = new MutationObserver( function () {&lt;br /&gt;
            var $dialog = $( &#039;.ve-ui-mwSaveDialog:visible&#039; );&lt;br /&gt;
            if ( !$dialog.length ) return;&lt;br /&gt;
            if ( $dialog.data( &#039;summary-enforced&#039; ) ) return;&lt;br /&gt;
&lt;br /&gt;
            $dialog.data( &#039;summary-enforced&#039;, true );&lt;br /&gt;
&lt;br /&gt;
            var $summaryInput = $dialog.find( &#039;textarea[name=&amp;quot;wpSummary&amp;quot;], input[name=&amp;quot;wpSummary&amp;quot;]&#039; );&lt;br /&gt;
            if ( !$summaryInput.length ) {&lt;br /&gt;
                $summaryInput = $dialog.find( &#039;.ve-ui-mwSaveDialog-summary textarea, .ve-ui-mwSaveDialog-summary input&#039; );&lt;br /&gt;
            }&lt;br /&gt;
            if ( !$summaryInput.length ) return;&lt;br /&gt;
&lt;br /&gt;
            var $saveBtn = $dialog.find( &#039;.oo-ui-flaggedElement-primary button, button.oo-ui-flaggedElement-primary&#039; );&lt;br /&gt;
            if ( !$saveBtn.length ) {&lt;br /&gt;
                $saveBtn = $dialog.find( &#039;a.oo-ui-flaggedElement-primary&#039; );&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Add hint message above summary&lt;br /&gt;
            if ( !$dialog.find( &#039;.soji-summary-hint&#039; ).length ) {&lt;br /&gt;
                $summaryInput.before(&lt;br /&gt;
                    &#039;&amp;lt;div class=&amp;quot;soji-summary-hint&amp;quot; style=&amp;quot;background:#fff3cd; color:#856404; padding:8px 12px; border-left:4px solid #ffc107; margin-bottom:8px; font-size:0.9em; border-radius:3px;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                    &#039;⚠ &amp;lt;strong&amp;gt;Edit summary required.&amp;lt;/strong&amp;gt; Describe what you changed (minimum &#039; + MIN_LENGTH + &#039; characters). Used for Revision History.&#039; +&lt;br /&gt;
                    &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                );&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Add counter&lt;br /&gt;
            if ( !$dialog.find( &#039;.soji-summary-counter&#039; ).length ) {&lt;br /&gt;
                $summaryInput.after(&lt;br /&gt;
                    &#039;&amp;lt;div class=&amp;quot;soji-summary-counter&amp;quot; style=&amp;quot;font-size:0.85em; margin-top:4px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                );&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            function updateState() {&lt;br /&gt;
                var len = $summaryInput.val().trim().length;&lt;br /&gt;
                var $counter = $dialog.find( &#039;.soji-summary-counter&#039; );&lt;br /&gt;
&lt;br /&gt;
                if ( len === 0 ) {&lt;br /&gt;
                    $counter.html( &#039;&amp;lt;span style=&amp;quot;color:#d33&amp;quot;&amp;gt;⚠ Empty — please describe your changes&amp;lt;/span&amp;gt;&#039; );&lt;br /&gt;
                    disableSaveButton( $saveBtn );&lt;br /&gt;
                } else if ( len &amp;lt; MIN_LENGTH ) {&lt;br /&gt;
                    $counter.html( &#039;&amp;lt;span style=&amp;quot;color:#f80&amp;quot;&amp;gt;⚠ Too short (&#039; + len + &#039;/&#039; + MIN_LENGTH + &#039; chars minimum)&amp;lt;/span&amp;gt;&#039; );&lt;br /&gt;
                    disableSaveButton( $saveBtn );&lt;br /&gt;
                } else {&lt;br /&gt;
                    $counter.html( &#039;&amp;lt;span style=&amp;quot;color:#0a0&amp;quot;&amp;gt;✓ &#039; + len + &#039; characters — good&amp;lt;/span&amp;gt;&#039; );&lt;br /&gt;
                    enableSaveButton( $saveBtn );&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            $summaryInput.on( &#039;input keyup change&#039;, updateState );&lt;br /&gt;
            updateState();&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        observer.observe( document.body, { childList: true, subtree: true } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function disableSaveButton( $btn ) {&lt;br /&gt;
        $btn.css( &#039;opacity&#039;, &#039;0.5&#039; ).css( &#039;pointer-events&#039;, &#039;none&#039; );&lt;br /&gt;
        $btn.attr( &#039;aria-disabled&#039;, &#039;true&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function enableSaveButton( $btn ) {&lt;br /&gt;
        $btn.css( &#039;opacity&#039;, &#039;&#039; ).css( &#039;pointer-events&#039;, &#039;&#039; );&lt;br /&gt;
        $btn.removeAttr( &#039;aria-disabled&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( setupVEEnforcement );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Edit Draft Auto-Save (localStorage)&lt;br /&gt;
 *  Save draft mỗi 30s, restore khi mở lại edit&lt;br /&gt;
 *  Backup cho trường hợp stash expire hoặc browser crash&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var SAVE_INTERVAL = 30 * 1000;  // 30 seconds&lt;br /&gt;
    var DRAFT_TTL    = 30 * 24 * 60 * 60 * 1000;  // 30 days&lt;br /&gt;
    var STORAGE_PREFIX = &#039;mw-draft-&#039;;&lt;br /&gt;
&lt;br /&gt;
    function getDraftKey() {&lt;br /&gt;
        return STORAGE_PREFIX + mw.config.get( &#039;wgPageName&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function saveDraft( content ) {&lt;br /&gt;
        try {&lt;br /&gt;
            var data = {&lt;br /&gt;
                content: content,&lt;br /&gt;
                timestamp: Date.now(),&lt;br /&gt;
                page: mw.config.get( &#039;wgPageName&#039; ),&lt;br /&gt;
                user: mw.config.get( &#039;wgUserName&#039; )&lt;br /&gt;
            };&lt;br /&gt;
            localStorage.setItem( getDraftKey(), JSON.stringify( data ) );&lt;br /&gt;
            return true;&lt;br /&gt;
        } catch ( e ) {&lt;br /&gt;
            console.warn( &#039;Failed to save draft:&#039;, e );&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadDraft() {&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = localStorage.getItem( getDraftKey() );&lt;br /&gt;
            if ( !raw ) return null;&lt;br /&gt;
            var data = JSON.parse( raw );&lt;br /&gt;
            if ( Date.now() - data.timestamp &amp;gt; DRAFT_TTL ) {&lt;br /&gt;
                localStorage.removeItem( getDraftKey() );&lt;br /&gt;
                return null;&lt;br /&gt;
            }&lt;br /&gt;
            return data;&lt;br /&gt;
        } catch ( e ) {&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearDraft() {&lt;br /&gt;
        try {&lt;br /&gt;
            localStorage.removeItem( getDraftKey() );&lt;br /&gt;
        } catch ( e ) {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function formatAge( ts ) {&lt;br /&gt;
        var diff = Date.now() - ts;&lt;br /&gt;
        var mins = Math.floor( diff / 60000 );&lt;br /&gt;
        if ( mins &amp;lt; 1 ) return &#039;just now&#039;;&lt;br /&gt;
        if ( mins &amp;lt; 60 ) return mins + &#039; min ago&#039;;&lt;br /&gt;
        var hrs = Math.floor( mins / 60 );&lt;br /&gt;
        if ( hrs &amp;lt; 24 ) return hrs + &#039; hour&#039; + ( hrs &amp;gt; 1 ? &#039;s&#039; : &#039;&#039; ) + &#039; ago&#039;;&lt;br /&gt;
        var days = Math.floor( hrs / 24 );&lt;br /&gt;
        return days + &#039; day&#039; + ( days &amp;gt; 1 ? &#039;s&#039; : &#039;&#039; ) + &#039; ago&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupSourceEditor() {&lt;br /&gt;
        var $textarea = $( &#039;#wpTextbox1&#039; );&lt;br /&gt;
        if ( !$textarea.length ) return;&lt;br /&gt;
&lt;br /&gt;
        // Restore draft on load&lt;br /&gt;
        var draft = loadDraft();&lt;br /&gt;
        if ( draft &amp;amp;&amp;amp; draft.content !== $textarea.val() ) {&lt;br /&gt;
            var age = formatAge( draft.timestamp );&lt;br /&gt;
            var $banner = $(&lt;br /&gt;
                &#039;&amp;lt;div class=&amp;quot;mw-draft-banner&amp;quot; style=&amp;quot;background:#fff3cd; border-left:4px solid #ffc107; padding:10px 14px; margin:8px 0; border-radius:3px;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;strong&amp;gt;📝 Unsaved draft found&amp;lt;/strong&amp;gt; (&#039; + age + &#039;)&amp;lt;br&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;mw-draft-restore&amp;quot; style=&amp;quot;margin-top:6px; margin-right:8px;&amp;quot;&amp;gt;Restore draft&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;mw-draft-discard&amp;quot; style=&amp;quot;margin-top:6px;&amp;quot;&amp;gt;Discard draft&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            );&lt;br /&gt;
            $textarea.before( $banner );&lt;br /&gt;
&lt;br /&gt;
            $banner.find( &#039;.mw-draft-restore&#039; ).on( &#039;click&#039;, function () {&lt;br /&gt;
                $textarea.val( draft.content );&lt;br /&gt;
                $banner.remove();&lt;br /&gt;
            } );&lt;br /&gt;
            $banner.find( &#039;.mw-draft-discard&#039; ).on( &#039;click&#039;, function () {&lt;br /&gt;
                clearDraft();&lt;br /&gt;
                $banner.remove();&lt;br /&gt;
            } );&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Auto-save every 30s&lt;br /&gt;
        var saveTimer = setInterval( function () {&lt;br /&gt;
            saveDraft( $textarea.val() );&lt;br /&gt;
            showSaveIndicator();&lt;br /&gt;
        }, SAVE_INTERVAL );&lt;br /&gt;
&lt;br /&gt;
        // Save on every change (throttled)&lt;br /&gt;
        var changeTimer;&lt;br /&gt;
        $textarea.on( &#039;input&#039;, function () {&lt;br /&gt;
            clearTimeout( changeTimer );&lt;br /&gt;
            changeTimer = setTimeout( function () {&lt;br /&gt;
                saveDraft( $textarea.val() );&lt;br /&gt;
            }, 2000 );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        // Clear draft on successful save&lt;br /&gt;
        $( &#039;#editform&#039; ).on( &#039;submit&#039;, function () {&lt;br /&gt;
            // Wait a bit, then clear if save successful (page redirected)&lt;br /&gt;
            setTimeout( clearDraft, 500 );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        // Save on tab close&lt;br /&gt;
        window.addEventListener( &#039;beforeunload&#039;, function () {&lt;br /&gt;
            saveDraft( $textarea.val() );&lt;br /&gt;
        } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupVisualEditor() {&lt;br /&gt;
        // VE saves to its own state, but we can backup the wikitext periodically&lt;br /&gt;
        mw.hook( &#039;ve.activationComplete&#039; ).add( function () {&lt;br /&gt;
            var saveTimer = setInterval( function () {&lt;br /&gt;
                if ( !mw.libs.ve || !mw.libs.ve.targetLoader ) return;&lt;br /&gt;
                try {&lt;br /&gt;
                    var target = ve.init.target;&lt;br /&gt;
                    if ( target &amp;amp;&amp;amp; target.getSurface ) {&lt;br /&gt;
                        var surface = target.getSurface();&lt;br /&gt;
                        if ( surface ) {&lt;br /&gt;
                            var html = surface.getHtml();&lt;br /&gt;
                            saveDraft( html );&lt;br /&gt;
                            showSaveIndicator();&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                } catch ( e ) {}&lt;br /&gt;
            }, SAVE_INTERVAL );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        mw.hook( &#039;ve.deactivationComplete&#039; ).add( function () {&lt;br /&gt;
            // Don&#039;t clear automatically — user may want to reopen&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        mw.hook( &#039;ve.saveComplete&#039; ).add( function () {&lt;br /&gt;
            clearDraft();&lt;br /&gt;
        } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showSaveIndicator() {&lt;br /&gt;
        var $existing = $( &#039;#mw-draft-indicator&#039; );&lt;br /&gt;
        if ( $existing.length ) {&lt;br /&gt;
            $existing.show().delay( 2000 ).fadeOut( 500 );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        var $indicator = $(&lt;br /&gt;
            &#039;&amp;lt;div id=&amp;quot;mw-draft-indicator&amp;quot; style=&amp;quot;position:fixed; bottom:20px; right:20px; background:#0a0; color:white; padding:6px 12px; border-radius:4px; font-size:0.85em; z-index:9999; box-shadow:0 2px 8px rgba(0,0,0,0.2);&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;💾 Draft saved locally&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
        );&lt;br /&gt;
        $( &#039;body&#039; ).append( $indicator );&lt;br /&gt;
        $indicator.delay( 2000 ).fadeOut( 500, function () { $( this ).remove(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( function () {&lt;br /&gt;
        var action = mw.config.get( &#039;wgAction&#039; );&lt;br /&gt;
        if ( action === &#039;edit&#039; || action === &#039;submit&#039; ) {&lt;br /&gt;
            setupSourceEditor();&lt;br /&gt;
        }&lt;br /&gt;
        if ( mw.config.get( &#039;wgVisualEditor&#039; ) ) {&lt;br /&gt;
            setupVisualEditor();&lt;br /&gt;
        }&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  MultiBoilerplate auto-load on dropdown change&lt;br /&gt;
 *  Use URL navigation instead of form submission (more reliable)&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    $( function () {&lt;br /&gt;
        // Only run on edit action&lt;br /&gt;
        if ( mw.config.get( &#039;wgAction&#039; ) !== &#039;edit&#039; ) return;&lt;br /&gt;
&lt;br /&gt;
        // Find MultiBoilerplate dropdown — try multiple selectors&lt;br /&gt;
        var $select = $( &#039;select[name=&amp;quot;boilerplate&amp;quot;], #multiboilerplate-select, #boilerplate-select&#039; );&lt;br /&gt;
&lt;br /&gt;
        if ( !$select.length ) {&lt;br /&gt;
            console.log( &#039;[MB-AutoLoad] No boilerplate dropdown found on this page&#039; );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        console.log( &#039;[MB-AutoLoad] Found dropdown:&#039;, $select[ 0 ] );&lt;br /&gt;
&lt;br /&gt;
        // Find Load button to hide it&lt;br /&gt;
        var $form = $select.closest( &#039;form&#039; );&lt;br /&gt;
        var $submitBtn = $form.find( &#039;input[type=&amp;quot;submit&amp;quot;], button[type=&amp;quot;submit&amp;quot;]&#039; ).filter( function () {&lt;br /&gt;
            var text = ( $( this ).val() || $( this ).text() || &#039;&#039; ).toLowerCase();&lt;br /&gt;
            return text.indexOf( &#039;load&#039; ) !== -1 || text.indexOf( &#039;boilerplate&#039; ) !== -1;&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        $submitBtn.hide();&lt;br /&gt;
&lt;br /&gt;
        // Add loading indicator&lt;br /&gt;
        var $indicator = $(&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;mb-loading-indicator&amp;quot; style=&amp;quot;margin-left:8px; color:#0054A6; font-style:italic; display:none;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;⏳ Loading template...&#039; +&lt;br /&gt;
            &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
        );&lt;br /&gt;
        $select.after( $indicator );&lt;br /&gt;
&lt;br /&gt;
        // Auto-load on dropdown change&lt;br /&gt;
        $select.on( &#039;change&#039;, function () {&lt;br /&gt;
            var value = $( this ).val();&lt;br /&gt;
&lt;br /&gt;
            console.log( &#039;[MB-AutoLoad] Selected:&#039;, value );&lt;br /&gt;
&lt;br /&gt;
            if ( !value || value === &#039;&#039; || value === &#039;-&#039; ) {&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Show loading&lt;br /&gt;
            $indicator.show();&lt;br /&gt;
            $select.prop( &#039;disabled&#039;, true );&lt;br /&gt;
&lt;br /&gt;
            // Build URL with boilerplate parameter&lt;br /&gt;
            var url = new URL( window.location.href );&lt;br /&gt;
            url.searchParams.set( &#039;boilerplate&#039;, value );&lt;br /&gt;
&lt;br /&gt;
            console.log( &#039;[MB-AutoLoad] Navigating to:&#039;, url.toString() );&lt;br /&gt;
&lt;br /&gt;
            // Small delay for visual feedback&lt;br /&gt;
            setTimeout( function () {&lt;br /&gt;
                window.location.href = url.toString();&lt;br /&gt;
            }, 300 );&lt;br /&gt;
        } );&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
/* Auto-convert spaces to dashes in software-card filename hints */&lt;br /&gt;
$(function() {&lt;br /&gt;
    $(&#039;.software-card-no-icon-text code&#039;).each(function() {&lt;br /&gt;
        var text = $(this).text();&lt;br /&gt;
        // Replace spaces with dashes&lt;br /&gt;
        $(this).text(text.replace(/ /g, &#039;-&#039;));&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Filename hint formatter&lt;br /&gt;
 *  Convert spaces → dashes, strip special chars&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function cleanFilename( text ) {&lt;br /&gt;
        return text&lt;br /&gt;
            .replace( /&amp;amp;amp;/g, &#039;&amp;amp;&#039; )      // decode HTML entity first&lt;br /&gt;
            .replace( /&amp;amp;#38;/g, &#039;&amp;amp;&#039; )&lt;br /&gt;
            .replace( / &amp;amp; /g, &#039;-&#039; )         // &amp;quot; &amp;amp; &amp;quot; → &amp;quot;-&amp;quot;&lt;br /&gt;
            .replace( /&amp;amp;/g, &#039;&#039; )            // remove remaining &amp;amp;&lt;br /&gt;
            .replace( / /g, &#039;-&#039; )           // space → dash&lt;br /&gt;
            .replace( /-+/g, &#039;-&#039; );         // multiple dashes → single&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function formatFilenameHints() {&lt;br /&gt;
        var selectors = [&lt;br /&gt;
            &#039;.cert-card-missing code&#039;,&lt;br /&gt;
            &#039;.software-card-no-icon-text code&#039;,&lt;br /&gt;
            &#039;.accessory-card-no-icon-text code&#039;,&lt;br /&gt;
            &#039;.product-no-image-text code&#039;,&lt;br /&gt;
            &#039;.faq-card-no-icon-text code&#039;&lt;br /&gt;
        ].join( &#039;, &#039; );&lt;br /&gt;
&lt;br /&gt;
        $( selectors ).each( function () {&lt;br /&gt;
            var $code = $( this );&lt;br /&gt;
            if ( $code.data( &#039;filename-formatted&#039; ) ) return;&lt;br /&gt;
&lt;br /&gt;
            var text = $code.text();&lt;br /&gt;
            var formatted = cleanFilename( text );&lt;br /&gt;
&lt;br /&gt;
            if ( formatted !== text ) {&lt;br /&gt;
                $code.text( formatted );&lt;br /&gt;
            }&lt;br /&gt;
            $code.data( &#039;filename-formatted&#039;, &#039;1&#039; );&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( formatFilenameHints );&lt;br /&gt;
&lt;br /&gt;
    if ( window.MutationObserver ) {&lt;br /&gt;
        var observer = new MutationObserver( function () {&lt;br /&gt;
            formatFilenameHints();&lt;br /&gt;
        });&lt;br /&gt;
        observer.observe( document.body, { childList: true, subtree: true } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
} )( jQuery );&lt;br /&gt;
&lt;br /&gt;
/* Real-time filter for dynamically loaded sidebar items */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var BLOCKED_PATTERNS = [&lt;br /&gt;
        &#039;Product_Change_Notifications&#039;,&lt;br /&gt;
        &#039;Product Change Notifications&#039;,&lt;br /&gt;
        &#039;Firmware_Changelog&#039;,&lt;br /&gt;
        &#039;Firmware Changelog&#039;,&lt;br /&gt;
        &#039;Promotional_Material&#039;,&lt;br /&gt;
        &#039;Promotional Material&#039;,&lt;br /&gt;
        &#039;Certifications&#039;,&lt;br /&gt;
        &#039;Certification_%26_Approvals&#039;,&lt;br /&gt;
        &#039;Certification &amp;amp; Approvals&#039;&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function hideBlocked( context ) {&lt;br /&gt;
        var $context = context ? $( context ) : $( document );&lt;br /&gt;
        $context.find( &#039;#p-categorytree-portlet a, #mw-panel a&#039; ).each( function () {&lt;br /&gt;
            var $link = $( this );&lt;br /&gt;
            var href = $link.attr( &#039;href&#039; ) || &#039;&#039;;&lt;br /&gt;
            var text = $link.text();&lt;br /&gt;
&lt;br /&gt;
            for ( var i = 0; i &amp;lt; BLOCKED_PATTERNS.length; i++ ) {&lt;br /&gt;
                var pattern = BLOCKED_PATTERNS[ i ];&lt;br /&gt;
                if ( href.indexOf( pattern ) !== -1 || text.indexOf( pattern.replace( /_/g, &#039; &#039; ) ) !== -1 ) {&lt;br /&gt;
                    $link.closest( &#039;li, .CategoryTreeItem&#039; ).addClass( &#039;sidebar-hidden&#039; );&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Run immediately&lt;br /&gt;
    hideBlocked();&lt;br /&gt;
&lt;br /&gt;
    // Observe sidebar for dynamic content&lt;br /&gt;
    if ( window.MutationObserver ) {&lt;br /&gt;
        var sidebar = document.querySelector( &#039;#p-categorytree-portlet, #mw-panel&#039; );&lt;br /&gt;
        if ( sidebar ) {&lt;br /&gt;
            new MutationObserver( function ( mutations ) {&lt;br /&gt;
                mutations.forEach( function ( m ) {&lt;br /&gt;
                    if ( m.addedNodes.length &amp;gt; 0 ) {&lt;br /&gt;
                        hideBlocked( m.target );&lt;br /&gt;
                    }&lt;br /&gt;
                });&lt;br /&gt;
            }).observe( sidebar, {&lt;br /&gt;
                childList: true,&lt;br /&gt;
                subtree: true&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
})( jQuery );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Auto-play video on scroll into view (IntersectionObserver)&lt;br /&gt;
 *  Play when visible, pause when scrolled out, loop infinitely&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function setupScrollVideos() {&lt;br /&gt;
        if ( !( &#039;IntersectionObserver&#039; in window ) ) return;&lt;br /&gt;
&lt;br /&gt;
        var videos = document.querySelectorAll( &#039;video.scroll-play, .scroll-play video&#039; );&lt;br /&gt;
        if ( !videos.length ) return;&lt;br /&gt;
&lt;br /&gt;
        var observer = new IntersectionObserver( function ( entries ) {&lt;br /&gt;
            entries.forEach( function ( entry ) {&lt;br /&gt;
                var video = entry.target;&lt;br /&gt;
                if ( entry.isIntersecting ) {&lt;br /&gt;
                    video.play().catch( function () {&lt;br /&gt;
                        // Autoplay blocked by browser, mute and try again&lt;br /&gt;
                        video.muted = true;&lt;br /&gt;
                        video.play();&lt;br /&gt;
                    });&lt;br /&gt;
                } else {&lt;br /&gt;
                    video.pause();&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }, {&lt;br /&gt;
            threshold: 0.5  // play when 50% visible&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        videos.forEach( function ( video ) {&lt;br /&gt;
            video.muted = true;        // required for autoplay in most browsers&lt;br /&gt;
            video.loop = true;          // infinite loop&lt;br /&gt;
            video.playsInline = true;   // iOS Safari support&lt;br /&gt;
            observer.observe( video );&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( document.readyState !== &#039;loading&#039; ) {&lt;br /&gt;
        setupScrollVideos();&lt;br /&gt;
    } else {&lt;br /&gt;
        document.addEventListener( &#039;DOMContentLoaded&#039;, setupScrollVideos );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
} )();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* =========================&lt;br /&gt;
   YOUTUBE EMBED via JS&lt;br /&gt;
   Template chỉ output data, JS generate full embed&lt;br /&gt;
   ========================= */&lt;br /&gt;
(function () {&lt;br /&gt;
    function buildEmbed(container) {&lt;br /&gt;
        if (container.dataset.initialized === &#039;1&#039;) return;&lt;br /&gt;
        container.dataset.initialized = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
        var videoId = container.dataset.videoId;&lt;br /&gt;
        var width = container.dataset.width || &#039;640&#039;;&lt;br /&gt;
        var align = container.dataset.align || &#039;center&#039;;&lt;br /&gt;
        var caption = container.dataset.caption || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (!videoId) {&lt;br /&gt;
            container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#d33&amp;quot;&amp;gt;Error: missing video ID&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Build container styles&lt;br /&gt;
        container.style.textAlign = align;&lt;br /&gt;
        container.style.margin = &#039;12px 0&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Thumbnail wrapper&lt;br /&gt;
        var thumbWrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        thumbWrap.className = &#039;soji-yt-thumb-wrap&#039;;&lt;br /&gt;
        thumbWrap.style.cssText = &lt;br /&gt;
            &#039;position: relative;&#039; +&lt;br /&gt;
            &#039;display: inline-block;&#039; +&lt;br /&gt;
            &#039;width: &#039; + width + &#039;px;&#039; +&lt;br /&gt;
            &#039;max-width: 100%;&#039; +&lt;br /&gt;
            &#039;aspect-ratio: 16/9;&#039; +&lt;br /&gt;
            &#039;cursor: pointer;&#039; +&lt;br /&gt;
            &#039;overflow: hidden;&#039; +&lt;br /&gt;
            &#039;border-radius: 8px;&#039; +&lt;br /&gt;
            &#039;background: #000;&#039; +&lt;br /&gt;
            &#039;box-shadow: 0 4px 12px rgba(0,0,0,0.15);&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Thumbnail image&lt;br /&gt;
        var img = document.createElement(&#039;img&#039;);&lt;br /&gt;
        img.src = &#039;https://i.ytimg.com/vi/&#039; + videoId + &#039;/hqdefault.jpg&#039;;&lt;br /&gt;
        img.alt = &#039;Video thumbnail&#039;;&lt;br /&gt;
        img.loading = &#039;lazy&#039;;&lt;br /&gt;
        img.style.cssText = &lt;br /&gt;
            &#039;width: 100%;&#039; +&lt;br /&gt;
            &#039;height: 100%;&#039; +&lt;br /&gt;
            &#039;object-fit: cover;&#039; +&lt;br /&gt;
            &#039;display: block;&#039; +&lt;br /&gt;
            &#039;transition: transform 0.3s, filter 0.2s;&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Try maxresdefault for better quality&lt;br /&gt;
        var hiresTest = new Image();&lt;br /&gt;
        hiresTest.onload = function () {&lt;br /&gt;
            if (hiresTest.naturalWidth &amp;gt; 480) {&lt;br /&gt;
                img.src = &#039;https://i.ytimg.com/vi/&#039; + videoId + &#039;/maxresdefault.jpg&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        };&lt;br /&gt;
        hiresTest.src = &#039;https://i.ytimg.com/vi/&#039; + videoId + &#039;/maxresdefault.jpg&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Play button overlay&lt;br /&gt;
        var overlay = document.createElement(&#039;div&#039;);&lt;br /&gt;
        overlay.className = &#039;soji-yt-play-overlay&#039;;&lt;br /&gt;
        overlay.style.cssText = &lt;br /&gt;
            &#039;position: absolute;&#039; +&lt;br /&gt;
            &#039;top: 50%;&#039; +&lt;br /&gt;
            &#039;left: 50%;&#039; +&lt;br /&gt;
            &#039;transform: translate(-50%, -50%);&#039; +&lt;br /&gt;
            &#039;width: 72px;&#039; +&lt;br /&gt;
            &#039;height: 50px;&#039; +&lt;br /&gt;
            &#039;background: rgba(0, 0, 0, 0.7);&#039; +&lt;br /&gt;
            &#039;border-radius: 14px;&#039; +&lt;br /&gt;
            &#039;display: flex;&#039; +&lt;br /&gt;
            &#039;align-items: center;&#039; +&lt;br /&gt;
            &#039;justify-content: center;&#039; +&lt;br /&gt;
            &#039;transition: background 0.2s, transform 0.2s;&#039; +&lt;br /&gt;
            &#039;pointer-events: none;&#039;;&lt;br /&gt;
&lt;br /&gt;
        overlay.innerHTML = &lt;br /&gt;
            &#039;&amp;lt;svg width=&amp;quot;36&amp;quot; height=&amp;quot;26&amp;quot; viewBox=&amp;quot;0 0 36 26&amp;quot; fill=&amp;quot;white&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;polygon points=&amp;quot;14,7 14,19 25,13&amp;quot;/&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/svg&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
        thumbWrap.appendChild(img);&lt;br /&gt;
        thumbWrap.appendChild(overlay);&lt;br /&gt;
&lt;br /&gt;
        // Hover effect&lt;br /&gt;
        thumbWrap.addEventListener(&#039;mouseenter&#039;, function () {&lt;br /&gt;
            overlay.style.background = &#039;#f00&#039;;&lt;br /&gt;
            overlay.style.transform = &#039;translate(-50%, -50%) scale(1.1)&#039;;&lt;br /&gt;
            img.style.filter = &#039;brightness(0.85)&#039;;&lt;br /&gt;
        });&lt;br /&gt;
        thumbWrap.addEventListener(&#039;mouseleave&#039;, function () {&lt;br /&gt;
            overlay.style.background = &#039;rgba(0, 0, 0, 0.7)&#039;;&lt;br /&gt;
            overlay.style.transform = &#039;translate(-50%, -50%) scale(1)&#039;;&lt;br /&gt;
            img.style.filter = &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // Click → load iframe&lt;br /&gt;
        thumbWrap.addEventListener(&#039;click&#039;, function () {&lt;br /&gt;
            var iframe = document.createElement(&#039;iframe&#039;);&lt;br /&gt;
            iframe.src = &#039;https://www.youtube-nocookie.com/embed/&#039; + videoId + &lt;br /&gt;
                         &#039;?autoplay=1&amp;amp;rel=0&amp;amp;modestbranding=1&amp;amp;playsinline=1&#039;;&lt;br /&gt;
            iframe.style.cssText = &lt;br /&gt;
                &#039;width: 100%;&#039; +&lt;br /&gt;
                &#039;height: 100%;&#039; +&lt;br /&gt;
                &#039;border: 0;&#039; +&lt;br /&gt;
                &#039;position: absolute;&#039; +&lt;br /&gt;
                &#039;inset: 0;&#039;;&lt;br /&gt;
            iframe.setAttribute(&#039;frameborder&#039;, &#039;0&#039;);&lt;br /&gt;
            iframe.setAttribute(&#039;allow&#039;, &#039;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&#039;);&lt;br /&gt;
            iframe.setAttribute(&#039;allowfullscreen&#039;, &#039;true&#039;);&lt;br /&gt;
&lt;br /&gt;
            thumbWrap.innerHTML = &#039;&#039;;&lt;br /&gt;
            thumbWrap.appendChild(iframe);&lt;br /&gt;
            thumbWrap.style.cursor = &#039;default&#039;;&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        container.appendChild(thumbWrap);&lt;br /&gt;
&lt;br /&gt;
        // Caption&lt;br /&gt;
        if (caption) {&lt;br /&gt;
            var captionDiv = document.createElement(&#039;div&#039;);&lt;br /&gt;
            captionDiv.style.cssText = &lt;br /&gt;
                &#039;margin-top: 8px;&#039; +&lt;br /&gt;
                &#039;font-style: italic;&#039; +&lt;br /&gt;
                &#039;color: #666;&#039; +&lt;br /&gt;
                &#039;font-size: 0.9em;&#039;;&lt;br /&gt;
            captionDiv.textContent = caption;&lt;br /&gt;
            container.appendChild(captionDiv);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initYouTubeEmbeds() {&lt;br /&gt;
        document.querySelectorAll(&#039;.soji-yt-embed&#039;).forEach(buildEmbed);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, initYouTubeEmbeds);&lt;br /&gt;
    } else {&lt;br /&gt;
        initYouTubeEmbeds();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Floating Buttons: move to body level&lt;br /&gt;
 *  Escape mw-body-content stacking context&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
(function () {&lt;br /&gt;
    function moveFloatingButtonsToBody() {&lt;br /&gt;
        var fb = document.getElementById(&#039;floating-buttons&#039;);&lt;br /&gt;
        if (!fb) return;&lt;br /&gt;
        &lt;br /&gt;
        // Already at body level → skip&lt;br /&gt;
        if (fb.parentElement === document.body) return;&lt;br /&gt;
        &lt;br /&gt;
        document.body.appendChild(fb);&lt;br /&gt;
        &lt;br /&gt;
        // Force styles&lt;br /&gt;
        fb.style.position = &#039;fixed&#039;;&lt;br /&gt;
        fb.style.zIndex = &#039;99999&#039;;&lt;br /&gt;
        fb.style.bottom = &#039;24px&#039;;&lt;br /&gt;
        fb.style.right = &#039;24px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, moveFloatingButtonsToBody);&lt;br /&gt;
    } else {&lt;br /&gt;
        moveFloatingButtonsToBody();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Re-check sau 1s để chắc chắn (nếu floating-buttons được tạo bằng JS khác sau)&lt;br /&gt;
    setTimeout(moveFloatingButtonsToBody, 1000);&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
/* =========================&lt;br /&gt;
   SCROLL VIDEO - local video with autoplay on scroll&lt;br /&gt;
   ========================= */&lt;br /&gt;
(function () {&lt;br /&gt;
    function buildScrollVideo(container) {&lt;br /&gt;
        if (container.dataset.initialized === &#039;1&#039;) return;&lt;br /&gt;
        container.dataset.initialized = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
        var src = container.dataset.src;&lt;br /&gt;
        var width = container.dataset.width || &#039;640&#039;;&lt;br /&gt;
        var caption = container.dataset.caption || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (!src) {&lt;br /&gt;
            container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#d33&amp;quot;&amp;gt;Error: missing video src&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        container.style.textAlign = &#039;center&#039;;&lt;br /&gt;
        container.style.margin = &#039;12px 0&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Create video element&lt;br /&gt;
        var video = document.createElement(&#039;video&#039;);&lt;br /&gt;
        video.className = &#039;scroll-play&#039;;&lt;br /&gt;
        video.width = width;&lt;br /&gt;
        video.muted = true;&lt;br /&gt;
        video.loop = true;&lt;br /&gt;
        video.playsInline = true;&lt;br /&gt;
        video.preload = &#039;metadata&#039;;&lt;br /&gt;
        video.style.cssText = &#039;max-width:100%; border-radius:8px; box-shadow:0 2px 8px rgba(0,0,0,0.1);&#039;;&lt;br /&gt;
&lt;br /&gt;
        var source = document.createElement(&#039;source&#039;);&lt;br /&gt;
        source.src = src;&lt;br /&gt;
        source.type = &#039;video/mp4&#039;;&lt;br /&gt;
        video.appendChild(source);&lt;br /&gt;
        video.appendChild(document.createTextNode(&#039;Your browser does not support HTML5 video.&#039;));&lt;br /&gt;
&lt;br /&gt;
        container.appendChild(video);&lt;br /&gt;
&lt;br /&gt;
        // Caption&lt;br /&gt;
        if (caption) {&lt;br /&gt;
            var cap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            cap.style.cssText = &#039;margin-top:8px; font-style:italic; color:#666; font-size:0.9em;&#039;;&lt;br /&gt;
            cap.textContent = caption;&lt;br /&gt;
            container.appendChild(cap);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Setup IntersectionObserver for scroll autoplay&lt;br /&gt;
        if (&#039;IntersectionObserver&#039; in window) {&lt;br /&gt;
            var observer = new IntersectionObserver(function (entries) {&lt;br /&gt;
                entries.forEach(function (entry) {&lt;br /&gt;
                    if (entry.isIntersecting) {&lt;br /&gt;
                        video.play().catch(function () {});&lt;br /&gt;
                    } else {&lt;br /&gt;
                        video.pause();&lt;br /&gt;
                    }&lt;br /&gt;
                });&lt;br /&gt;
            }, { threshold: 0.5 });&lt;br /&gt;
            observer.observe(video);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initScrollVideos() {&lt;br /&gt;
        document.querySelectorAll(&#039;.soji-scroll-video&#039;).forEach(buildScrollVideo);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, initScrollVideos);&lt;br /&gt;
    } else {&lt;br /&gt;
        initScrollVideos();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
/* ========================================================================&lt;br /&gt;
 * Release Marker Checkbox — Edit form (Source + VisualEditor save dialog)&lt;br /&gt;
 * Tick → append [release] to edit summary → revision shows in history&lt;br /&gt;
 * ======================================================================== */&lt;br /&gt;
( function () {&lt;br /&gt;
	var MARKER = &#039;[release]&#039;;&lt;br /&gt;
	var lastDialogVisible = false;&lt;br /&gt;
&lt;br /&gt;
	function isVisible( el ) {&lt;br /&gt;
		return el &amp;amp;&amp;amp; el.offsetParent !== null;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function isSummaryField( el ) {&lt;br /&gt;
		if ( !el || !isVisible( el ) ) { return false; }&lt;br /&gt;
&lt;br /&gt;
		var id = ( el.id || &#039;&#039; ).toLowerCase();&lt;br /&gt;
		var name = ( el.name || &#039;&#039; ).toLowerCase();&lt;br /&gt;
		if ( id.indexOf( &#039;summary&#039; ) !== -1 || name.indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
&lt;br /&gt;
		var p = el.parentElement;&lt;br /&gt;
		for ( var i = 0; i &amp;lt; 6 &amp;amp;&amp;amp; p; i++ ) {&lt;br /&gt;
			var cls = ( p.className || &#039;&#039; ).toString().toLowerCase();&lt;br /&gt;
			if ( cls.indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
			p = p.parentElement;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var lblId = el.getAttribute( &#039;aria-labelledby&#039; );&lt;br /&gt;
		if ( lblId ) {&lt;br /&gt;
			var lblEl = document.getElementById( lblId );&lt;br /&gt;
			if ( lblEl &amp;amp;&amp;amp; lblEl.textContent.toLowerCase().indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var fl = el.closest( &#039;.oo-ui-fieldLayout&#039; );&lt;br /&gt;
		if ( fl ) {&lt;br /&gt;
			var label = fl.querySelector( &#039;.oo-ui-labelElement-label, label&#039; );&lt;br /&gt;
			if ( label &amp;amp;&amp;amp; label.textContent.toLowerCase().indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function findSummaryField() {&lt;br /&gt;
		var all = document.querySelectorAll( &#039;textarea, input[type=&amp;quot;text&amp;quot;]&#039; );&lt;br /&gt;
		for ( var i = 0; i &amp;lt; all.length; i++ ) {&lt;br /&gt;
			if ( isSummaryField( all[ i ] ) ) { return all[ i ]; }&lt;br /&gt;
		}&lt;br /&gt;
		return null;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function getContainer( field ) {&lt;br /&gt;
		return field.closest( &#039;.ve-ui-mwSaveDialog, .oo-ui-dialog-content, .editOptions, form&#039; )&lt;br /&gt;
			|| ( field.parentElement &amp;amp;&amp;amp; field.parentElement.parentElement )&lt;br /&gt;
			|| document;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function cleanupStaleWrappers() {&lt;br /&gt;
		document.querySelectorAll( &#039;.release-checkbox-wrapper&#039; ).forEach( function ( w ) {&lt;br /&gt;
			if ( !isVisible( w ) ) { w.remove(); }&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function resetAllCheckboxes() {&lt;br /&gt;
		document.querySelectorAll( &#039;.mark-as-release-cb&#039; ).forEach( function ( cb ) {&lt;br /&gt;
			cb.checked = false;&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function injectCheckbox( field ) {&lt;br /&gt;
		if ( !field ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var anchor =&lt;br /&gt;
			field.closest( &#039;.ve-ui-mwSaveDialog-summary&#039; ) ||&lt;br /&gt;
			field.closest( &#039;.oo-ui-fieldLayout&#039; ) ||&lt;br /&gt;
			field.closest( &#039;.oo-ui-textInputWidget&#039; ) ||&lt;br /&gt;
			field.parentElement;&lt;br /&gt;
		if ( !anchor || !anchor.parentNode ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var container = getContainer( field );&lt;br /&gt;
		var existing = container.querySelector( &#039;.release-checkbox-wrapper&#039; );&lt;br /&gt;
		if ( existing &amp;amp;&amp;amp; isVisible( existing ) ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var wrapper = document.createElement( &#039;div&#039; );&lt;br /&gt;
		wrapper.className = &#039;release-checkbox-wrapper&#039;;&lt;br /&gt;
		wrapper.style.cssText = &#039;margin:8px 0;padding:8px 12px;background:#f0f8ff;border-left:3px solid #1f4e79;border-radius:3px;font-size:13px;display:block;box-sizing:border-box;&#039;;&lt;br /&gt;
		wrapper.innerHTML =&lt;br /&gt;
			&#039;&amp;lt;label style=&amp;quot;cursor:pointer;font-weight:500;display:block;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
			&#039;&amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;mark-as-release-cb&amp;quot; style=&amp;quot;margin-right:6px;vertical-align:middle;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
			&#039;Mark as published revision&#039; +&lt;br /&gt;
			&#039;&amp;lt;/label&amp;gt;&#039; +&lt;br /&gt;
			&#039;&amp;lt;div style=&amp;quot;font-size:11px;color:#666;margin-top:3px;margin-left:22px;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
			&#039;Tick to add this edit to the Revision History table.&#039; +&lt;br /&gt;
			&#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
		anchor.parentNode.insertBefore( wrapper, anchor );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function applyMarkerIfChecked() {&lt;br /&gt;
		var field = findSummaryField();&lt;br /&gt;
		if ( !field ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var container = getContainer( field );&lt;br /&gt;
		var cb = container.querySelector( &#039;.mark-as-release-cb&#039; );&lt;br /&gt;
		if ( !cb || !isVisible( cb ) || !cb.checked ) { return; }&lt;br /&gt;
&lt;br /&gt;
		if ( field.value.indexOf( MARKER ) === -1 ) {&lt;br /&gt;
			field.value = ( field.value.trim() ? field.value.trim() + &#039; &#039; : &#039;&#039; ) + MARKER;&lt;br /&gt;
			field.dispatchEvent( new Event( &#039;input&#039;, { bubbles: true } ) );&lt;br /&gt;
			field.dispatchEvent( new Event( &#039;change&#039;, { bubbles: true } ) );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// Reset checkbox after applying — prevents stale state on next save&lt;br /&gt;
		cb.checked = false;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	document.addEventListener( &#039;click&#039;, function ( e ) {&lt;br /&gt;
		var btn = e.target.closest( &#039;button, a[role=&amp;quot;button&amp;quot;], .oo-ui-buttonElement-button&#039; );&lt;br /&gt;
		if ( !btn ) { return; }&lt;br /&gt;
		var text = ( btn.textContent || &#039;&#039; ).trim().toLowerCase();&lt;br /&gt;
		if (&lt;br /&gt;
			text === &#039;save changes&#039; || text === &#039;save page&#039; ||&lt;br /&gt;
			text === &#039;publish changes&#039; || text === &#039;publish page&#039; ||&lt;br /&gt;
			text === &#039;lưu thay đổi&#039; || text === &#039;lưu trang&#039; ||&lt;br /&gt;
			btn.id === &#039;wpSave&#039;&lt;br /&gt;
		) {&lt;br /&gt;
			applyMarkerIfChecked();&lt;br /&gt;
		}&lt;br /&gt;
	}, true );&lt;br /&gt;
&lt;br /&gt;
	document.addEventListener( &#039;submit&#039;, function ( e ) {&lt;br /&gt;
		var form = e.target;&lt;br /&gt;
		if ( form &amp;amp;&amp;amp; ( form.id === &#039;editform&#039; || form.querySelector( &#039;#wpSummary&#039; ) ) ) {&lt;br /&gt;
			applyMarkerIfChecked();&lt;br /&gt;
		}&lt;br /&gt;
	}, true );&lt;br /&gt;
&lt;br /&gt;
	function scan() {&lt;br /&gt;
		cleanupStaleWrappers();&lt;br /&gt;
		var field = findSummaryField();&lt;br /&gt;
		var dialogVisible = !!field;&lt;br /&gt;
&lt;br /&gt;
		// Reset checkboxes when dialog transitions from closed -&amp;gt; open&lt;br /&gt;
		if ( dialogVisible &amp;amp;&amp;amp; !lastDialogVisible ) {&lt;br /&gt;
			resetAllCheckboxes();&lt;br /&gt;
		}&lt;br /&gt;
		lastDialogVisible = dialogVisible;&lt;br /&gt;
&lt;br /&gt;
		if ( field ) { injectCheckbox( field ); }&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if ( document.readyState === &#039;loading&#039; ) {&lt;br /&gt;
		document.addEventListener( &#039;DOMContentLoaded&#039;, scan );&lt;br /&gt;
	} else {&lt;br /&gt;
		scan();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	var observer = new MutationObserver( scan );&lt;br /&gt;
	observer.observe( document.body, {&lt;br /&gt;
		childList: true,&lt;br /&gt;
		subtree: true,&lt;br /&gt;
		attributes: true,&lt;br /&gt;
		attributeFilter: [ &#039;style&#039;, &#039;class&#039; ]&lt;br /&gt;
	} );&lt;br /&gt;
&lt;br /&gt;
	setInterval( scan, 500 );&lt;br /&gt;
}() );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ========================================================================&lt;br /&gt;
 * Revision History Renderer — show only revisions tagged with [release]&lt;br /&gt;
 * Auto-version v1.0, v1.1, v1.2 ... in chronological order&lt;br /&gt;
 * ======================================================================== */&lt;br /&gt;
( function () {&lt;br /&gt;
	function formatDate( ts ) {&lt;br /&gt;
		var d = new Date( ts );&lt;br /&gt;
		var dd = String( d.getDate() ).padStart( 2, &#039;0&#039; );&lt;br /&gt;
		var mm = String( d.getMonth() + 1 ).padStart( 2, &#039;0&#039; );&lt;br /&gt;
		var yyyy = d.getFullYear();&lt;br /&gt;
		return dd + &#039;/&#039; + mm + &#039;/&#039; + yyyy;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function hasReleaseMarker( comment ) {&lt;br /&gt;
		if ( !comment ) { return false; }&lt;br /&gt;
		return /(^|\s)\[release\](\s|$)/.test( comment );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function cleanComment( comment ) {&lt;br /&gt;
		if ( !comment ) { return &#039;Initial Release&#039;; }&lt;br /&gt;
		var cleaned = comment&lt;br /&gt;
			.replace( /\[release\]/g, &#039;&#039; )&lt;br /&gt;
			.replace( /\/\*[^*]*\*\//g, &#039;&#039; )&lt;br /&gt;
			.trim();&lt;br /&gt;
		return cleaned || &#039;Updated&#039;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function renderRevisionHistory( container ) {&lt;br /&gt;
		var api = new mw.Api();&lt;br /&gt;
		api.get( {&lt;br /&gt;
			action: &#039;query&#039;,&lt;br /&gt;
			prop: &#039;revisions&#039;,&lt;br /&gt;
			titles: mw.config.get( &#039;wgPageName&#039; ),&lt;br /&gt;
			rvprop: &#039;timestamp|user|comment|ids&#039;,&lt;br /&gt;
			rvlimit: 500&lt;br /&gt;
		} ).done( function ( data ) {&lt;br /&gt;
			var pages = data.query &amp;amp;&amp;amp; data.query.pages;&lt;br /&gt;
			if ( !pages ) { return; }&lt;br /&gt;
			var pageId = Object.keys( pages )[ 0 ];&lt;br /&gt;
			var revisions = pages[ pageId ].revisions || [];&lt;br /&gt;
&lt;br /&gt;
			var releases = revisions.filter( function ( r ) {&lt;br /&gt;
				return hasReleaseMarker( r.comment );&lt;br /&gt;
			} );&lt;br /&gt;
&lt;br /&gt;
			if ( !releases.length ) {&lt;br /&gt;
				container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#888;&amp;quot;&amp;gt;No published revisions yet. Tick &amp;quot;Mark as published revision&amp;quot; when saving an edit to add an entry here.&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			releases.sort( function ( a, b ) {&lt;br /&gt;
				return new Date( a.timestamp ) - new Date( b.timestamp );&lt;br /&gt;
			} );&lt;br /&gt;
			releases.forEach( function ( r, i ) {&lt;br /&gt;
				r.version = &#039;v1.&#039; + i;&lt;br /&gt;
			} );&lt;br /&gt;
			releases.reverse();&lt;br /&gt;
&lt;br /&gt;
			var html = &#039;&amp;lt;table class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
				&#039;&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Date&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Version&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Author&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&#039; +&lt;br /&gt;
				&#039;&amp;lt;tbody&amp;gt;&#039;;&lt;br /&gt;
			releases.forEach( function ( r ) {&lt;br /&gt;
				html +=&lt;br /&gt;
					&#039;&amp;lt;tr&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&#039; + formatDate( r.timestamp ) + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&amp;lt;strong&amp;gt;&#039; + r.version + &#039;&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&#039; + mw.html.escape( r.user ) + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&#039; + mw.html.escape( cleanComment( r.comment ) ) + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;/tr&amp;gt;&#039;;&lt;br /&gt;
			} );&lt;br /&gt;
			html += &#039;&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;&#039;;&lt;br /&gt;
			container.innerHTML = html;&lt;br /&gt;
		} ).fail( function () {&lt;br /&gt;
			container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#c00;&amp;quot;&amp;gt;Failed to load revision history.&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	mw.hook( &#039;wikipage.content&#039; ).add( function ( $content ) {&lt;br /&gt;
		$content.find( &#039;.revision-history-auto&#039; ).each( function () {&lt;br /&gt;
			renderRevisionHistory( this );&lt;br /&gt;
		} );&lt;br /&gt;
	} );&lt;br /&gt;
}() );&lt;br /&gt;
&lt;br /&gt;
/* ========================================================================&lt;br /&gt;
 * PDF TOC Generator with JS-measured page numbers&lt;br /&gt;
 * No forced page breaks - measures actual heading positions&lt;br /&gt;
 * ======================================================================== */&lt;br /&gt;
/* ===== PDF mode (pdf-service entry point) ===== */&lt;br /&gt;
( function () {&lt;br /&gt;
	&#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
	var params = new URLSearchParams( location.search );&lt;br /&gt;
	var pdfMode = params.get( &#039;pdfmode&#039; ) === &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
	if ( pdfMode ) {&lt;br /&gt;
		document.documentElement.classList.add( &#039;pdfmode&#039; );&lt;br /&gt;
		document.body &amp;amp;&amp;amp; document.body.classList.add( &#039;pdfmode&#039; );&lt;br /&gt;
		document.addEventListener( &#039;DOMContentLoaded&#039;, function () {&lt;br /&gt;
			document.body.classList.add( &#039;pdfmode&#039; );&lt;br /&gt;
		} );&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function addDownloadLink() {&lt;br /&gt;
		var title = ( mw.config &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
		var ns = mw.config &amp;amp;&amp;amp; mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
		if ( !title || ns !== 0 ) { return; }&lt;br /&gt;
&lt;br /&gt;
		// Show only on Datasheet / User Guide / Protocol pages, or Software &amp;amp; Applications category&lt;br /&gt;
		var titleMatches = /(Datasheet|User[_ ]Guide|Protocol)/i.test( title );&lt;br /&gt;
		var categories = ( mw.config &amp;amp;&amp;amp; mw.config.get( &#039;wgCategories&#039; ) ) || [];&lt;br /&gt;
		var inSoftwareCat = categories.some( function ( c ) {&lt;br /&gt;
			return /Software\s*&amp;amp;?\s*Applications/i.test( c );&lt;br /&gt;
		} );&lt;br /&gt;
		if ( !titleMatches &amp;amp;&amp;amp; !inSoftwareCat ) { return; }&lt;br /&gt;
&lt;br /&gt;
		if ( document.querySelector( &#039;.pdf-download-btn&#039; ) ) { return; }&lt;br /&gt;
		var anchor = document.querySelector( &#039;#firstHeading&#039; );&lt;br /&gt;
		if ( !anchor ) { return; }&lt;br /&gt;
		var btn = document.createElement( &#039;a&#039; );&lt;br /&gt;
		btn.className = &#039;pdf-download-btn&#039;;&lt;br /&gt;
		btn.href = &#039;/api/pdf?page=&#039; + encodeURIComponent( title );&lt;br /&gt;
		btn.target = &#039;_blank&#039;;&lt;br /&gt;
		btn.rel = &#039;noopener&#039;;&lt;br /&gt;
		btn.innerHTML = &#039;&amp;lt;span class=&amp;quot;pdf-download-icon&amp;quot;&amp;gt;\u2B07&amp;lt;/span&amp;gt; Download PDF&#039;;&lt;br /&gt;
		anchor.parentNode.insertBefore( btn, anchor.nextSibling );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if ( !pdfMode &amp;amp;&amp;amp; window.mw &amp;amp;&amp;amp; mw.hook ) {&lt;br /&gt;
		mw.hook( &#039;wikipage.content&#039; ).add( addDownloadLink );&lt;br /&gt;
	}&lt;br /&gt;
}() );&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Thứ tự mong muốn (viết thường)&lt;br /&gt;
    var ORDER = [&#039;datasheet&#039;, &#039;user guide&#039;, &#039;protocol&#039;, &#039;video&#039;];&lt;br /&gt;
&lt;br /&gt;
    function rank(text) {&lt;br /&gt;
        text = (text || &#039;&#039;).toLowerCase();&lt;br /&gt;
        for (var i = 0; i &amp;lt; ORDER.length; i++) {&lt;br /&gt;
            if (text.indexOf(ORDER[i]) !== -1) return i;&lt;br /&gt;
        }&lt;br /&gt;
        return ORDER.length; // loại chưa khai báo -&amp;gt; cuối&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Lấy text của label trực tiếp trong section (không lẫn con cháu)&lt;br /&gt;
    function keyText(sec) {&lt;br /&gt;
        for (var i = 0; i &amp;lt; sec.children.length; i++) {&lt;br /&gt;
            if (sec.children[i].classList.contains(&#039;CategoryTreeItem&#039;)) {&lt;br /&gt;
                return sec.children[i].textContent;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        return sec.textContent;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function sortContainer(container) {&lt;br /&gt;
        var sections = Array.prototype.filter.call(container.children, function (el) {&lt;br /&gt;
            return el.classList &amp;amp;&amp;amp; el.classList.contains(&#039;CategoryTreeSection&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        if (sections.length &amp;lt; 2) return;&lt;br /&gt;
&lt;br /&gt;
        var sorted = sections.slice().sort(function (a, b) {&lt;br /&gt;
            return rank(keyText(a)) - rank(keyText(b));&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // chỉ di chuyển nếu khác (tránh observer lặp vô hạn)&lt;br /&gt;
        var changed = sorted.some(function (el, i) { return el !== sections[i]; });&lt;br /&gt;
        if (changed) {&lt;br /&gt;
            sorted.forEach(function (el) { container.appendChild(el); });&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function run() {&lt;br /&gt;
        document.querySelectorAll(&#039;.CategoryTreeChildren&#039;).forEach(sortContainer);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    run();&lt;br /&gt;
&lt;br /&gt;
    // CategoryTree bung con kiểu lazy -&amp;gt; theo dõi &amp;amp; sắp lại&lt;br /&gt;
    var root = document.querySelector(&#039;#mw-panel&#039;) || document.body;&lt;br /&gt;
    if (root) {&lt;br /&gt;
        new MutationObserver(run).observe(root, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
});&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Protocol&amp;diff=2057</id>
		<title>LIGO PRO 2 Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Protocol&amp;diff=2057"/>
		<updated>2026-06-24T10:52:21Z</updated>

		<summary type="html">&lt;p&gt;Admin: Initial Release [release]&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&lt;br /&gt;
LLS Protocol is used in LIGO RS232 / RS485 fuel level sensor with the following parameters:&lt;br /&gt;
&lt;br /&gt;
* Baud rate: 2400,4800,9600,19200,38400,115200 (can be configured by software on PC)&lt;br /&gt;
* Data bits: 8&lt;br /&gt;
* Parity: None&lt;br /&gt;
* Stop bits: 1&lt;br /&gt;
* Flow control: None&lt;br /&gt;
&lt;br /&gt;
LIGO fuel level sensor has 2 working modes:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Slave mode:&#039;&#039;&#039; In this mode, the sensor will respond to all requests from the external device (master device). Each sensor in the network will be distinguished by different addresses configured with the LIGO Configurator software&lt;br /&gt;
# &#039;&#039;&#039;Master mode:&#039;&#039;&#039; In this mode the sensor will automatically send data to the external device with a preset interval (message interval)&lt;br /&gt;
&lt;br /&gt;
To enable this mode on the LIGO Configurator software, do the following:&lt;br /&gt;
&lt;br /&gt;
* Automatic transmission: select HEX (binary) or ASCII or ASCII EXT&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Automatic-Transmission.png|frameless|219px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Automatic transmission setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Set the time of each transfer (Message interval)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LLS-Protocol-Configurator-Message-Interval.png|frameless|228px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Message interval setting in LIGO Configurator&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Exchange Protocol Description ==&lt;br /&gt;
&lt;br /&gt;
The LLS protocol supports two types of exchange protocols: binary (HEX) and character view (transmission of ASCII sequences). It is recommended to use binary exchange protocol.&lt;br /&gt;
&lt;br /&gt;
=== Format of Binary Protocol Messages ===&lt;br /&gt;
&lt;br /&gt;
All the commands of the binary communication protocol have the same standardized format which is given in the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Sequential number of the field !! Field name !! Field size, byte !! Description&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 1 || Prefix || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is a marker of the message beginning shall have prefix 31h, and an outcoming messages shall be displayed with 3Eh prefix by the program.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 2 || Network address || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the network address of the recipient. &amp;lt;br&amp;gt;For prefix 3Eh specify the network address of the sender.&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 3 || Operation code || style=&amp;quot;text-align:center;&amp;quot; | 1 || For prefix 31h specify the code of operation which the program shall perform For Eh prefix specify the code of operation to which the response is given&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 4 || Data || style=&amp;quot;text-align:center;&amp;quot; | It depends on the operation code || Data composition and format of the field depends on the operation code&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:center;&amp;quot; | 5 || Checksum || style=&amp;quot;text-align:center;&amp;quot; | 1 || The field is used to control over data integrity&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Single-Stage Data Reading (command 06h) ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The data are transmitted with a lower byte ahead.&lt;br /&gt;
&lt;br /&gt;
==== Command format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format: ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 06h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output (command 07h) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to switch on periodic data output.&lt;br /&gt;
&lt;br /&gt;
After the command is processed, the sensor starts sending data periodically — level, temperature, and frequency — with the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power power supply (if the data output mode is not istalled by default).&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Periodic data output format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 07h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || -128…127 || style=&amp;quot;text-align:left;&amp;quot; | Temperature in in degrees Celsius&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Relative level&lt;br /&gt;
|-&lt;br /&gt;
| +6 || 2 || 0000h…FFFFh || style=&amp;quot;text-align:left;&amp;quot; | Frequency value&lt;br /&gt;
|-&lt;br /&gt;
| +8 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output Interval Adjustment (13h command) ===&lt;br /&gt;
&lt;br /&gt;
Command is designed to set up interval of periodic data output.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| +3 || 1 || 0…255 || style=&amp;quot;text-align:left;&amp;quot; | Interval of the data output in seconds&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 3Eh || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | The Network address of the sender&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 13h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Default Data Output Mode (command 17h) ===&lt;br /&gt;
&lt;br /&gt;
This command determines the order of data output after the sensor is powered on or the processor is reset. After the power is on or the processor is reset, the program will send data periodically via the interface at the time interval prescribed by the 13h command.&lt;br /&gt;
&lt;br /&gt;
==== Command format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;4&amp;quot; | +3 || rowspan=&amp;quot;4&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in binary form&lt;br /&gt;
|-&lt;br /&gt;
| 02h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII mode)&lt;br /&gt;
|-&lt;br /&gt;
| 03h || style=&amp;quot;text-align:left;&amp;quot; | The data are output in character-coded form (ASCII EXT mode)&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Response format ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset, bytes !! Field size, bytes !! Value !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || 1 || 31h || style=&amp;quot;text-align:left;&amp;quot; | Prefix&lt;br /&gt;
|-&lt;br /&gt;
| +1 || 1 || 00h..FFh || style=&amp;quot;text-align:left;&amp;quot; | Network address of the recipient&lt;br /&gt;
|-&lt;br /&gt;
| +2 || 1 || 17h || style=&amp;quot;text-align:left;&amp;quot; | Operation code&lt;br /&gt;
|-&lt;br /&gt;
| rowspan=&amp;quot;2&amp;quot; | +3 || rowspan=&amp;quot;2&amp;quot; | 1 || 00h || style=&amp;quot;text-align:left;&amp;quot; | The command has been executed successfully&lt;br /&gt;
|-&lt;br /&gt;
| 01h || style=&amp;quot;text-align:left;&amp;quot; | The command cannot be executed&lt;br /&gt;
|-&lt;br /&gt;
| +4 || 1 || 00h…FFh || style=&amp;quot;text-align:left;&amp;quot; | Checksum&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Description of Commands for the Text-Based Protocol ==&lt;br /&gt;
&lt;br /&gt;
Data exchange via the text-based protocol includes receipt and sending of ASCII symbols sequence interpreted and the request and response commands.&lt;br /&gt;
&lt;br /&gt;
=== Reading the Data ===&lt;br /&gt;
&lt;br /&gt;
The command is designed for reading of the current data: relative level, temperature, frequency. The command is a sequence of symbols ASCII &amp;quot;D&amp;quot; and &amp;quot;O&amp;quot;. After receipt of the &amp;quot;DO&amp;quot; command the program will response in the form of ASCII symbols sequence.&lt;br /&gt;
&lt;br /&gt;
For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. All values are in hexadecimal form.&lt;br /&gt;
&lt;br /&gt;
In case the frequency value exceeds FFFh, the data are considered invalid.&lt;br /&gt;
&lt;br /&gt;
=== Periodic Data Output ===&lt;br /&gt;
&lt;br /&gt;
The command is designed to switch on periodic data output. After processing the command, the sensor performs periodic data output in the text-based form (ASCII codes) of the following data: relative level, temperature, frequency.&lt;br /&gt;
&lt;br /&gt;
The data are being output periodically with an interval set up when cofiguring the sensor (LIGO Configurator software). In case the data output interval is set to zero, the data output won&#039;t be performed.&lt;br /&gt;
&lt;br /&gt;
Switching on of the periodic data output is done by sending of the &amp;quot;DP&amp;quot; symbols in line. After processing of the command, the symbols line will be received. For example, F=0AF9 t=1A N=03FF.0 &amp;amp;lt;CR&amp;amp;gt;&amp;amp;lt;LF&amp;amp;gt;, where F is the current frequency value, t is the current value of temperature in Celcius degrees, N is the level value. Turning off of the periodic data output is performed after receipt of any true command, reset of the processor or disconnection of power supply.&lt;br /&gt;
&lt;br /&gt;
== Checksum Calculation Algorithm ==&lt;br /&gt;
&lt;br /&gt;
The checksum is calculated using Dallas APPLICATION NOTE 27 table method: &#039;&#039;Understanding and Using Cyclic Redundancy Checks with Dallas Semiconductor iButton Products&#039;&#039;. One can use the following algorithms to calculate the checksum with a polynom &amp;lt;code&amp;gt;x^8 + x^5 + x^4 + 1&amp;lt;/code&amp;gt; (C language):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line&amp;gt;&lt;br /&gt;
U8 CRC8(U8 data, U8 crc)&lt;br /&gt;
{&lt;br /&gt;
    U8 i = data ^ crc;&lt;br /&gt;
    crc = 0;&lt;br /&gt;
    if (i &amp;amp; 0x01) crc ^= 0x5e;&lt;br /&gt;
    if (i &amp;amp; 0x02) crc ^= 0xbc;&lt;br /&gt;
    if (i &amp;amp; 0x04) crc ^= 0x61;&lt;br /&gt;
    if (i &amp;amp; 0x08) crc ^= 0xc2;&lt;br /&gt;
    if (i &amp;amp; 0x10) crc ^= 0x9d;&lt;br /&gt;
    if (i &amp;amp; 0x20) crc ^= 0x23;&lt;br /&gt;
    if (i &amp;amp; 0x40) crc ^= 0x46;&lt;br /&gt;
    if (i &amp;amp; 0x80) crc ^= 0x8c;&lt;br /&gt;
    return crc;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_2_Protocol&amp;diff=2056</id>
		<title>Category:LIGO PRO 2 Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_2_Protocol&amp;diff=2056"/>
		<updated>2026-06-24T10:52:11Z</updated>

		<summary type="html">&lt;p&gt;Admin: Auto-created category for LIGO PRO 2 Protocol&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- auto-created category --&amp;gt;&lt;br /&gt;
{{DEFAULTSORT:05 LIGO PRO 2 Protocol}}&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Protocol&amp;diff=2055</id>
		<title>LIGO PRO 2 Protocol</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Protocol&amp;diff=2055"/>
		<updated>2026-06-24T10:52:11Z</updated>

		<summary type="html">&lt;p&gt;Admin: Create page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== General Introduction ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brief introduction to the protocol per IEC/IEEE standards for wireless data protocols.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Protocol name and version&#039;&#039;&#039; — full name + version number (vd: SOJI Protocol v2.1)&lt;br /&gt;
* &#039;&#039;&#039;Purpose&#039;&#039;&#039; — what data the protocol transmits and to whom&lt;br /&gt;
* &#039;&#039;&#039;Transport layer&#039;&#039;&#039; — Bluetooth 5.4 BLE / WiFi / LoRa / proprietary RF&lt;br /&gt;
* &#039;&#039;&#039;Frequency &amp;amp; modulation&#039;&#039;&#039; — 2.4 GHz GFSK / LoRa CSS / ...&lt;br /&gt;
* &#039;&#039;&#039;Operating mode&#039;&#039;&#039; — Advertising-only (one-way broadcast) / Connection-based / Mesh&lt;br /&gt;
* &#039;&#039;&#039;Compatible devices&#039;&#039;&#039; — list of sensors / receivers supporting this protocol&lt;br /&gt;
* &#039;&#039;&#039;Reference standards&#039;&#039;&#039; — IEEE 802.15.4, Bluetooth Core 5.4, etc.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example structure:&#039;&#039;&#039;&lt;br /&gt;
* Paragraph 1: &amp;quot;The [Protocol Name] is a [type] protocol defined by SOJI Electronics for [purpose]...&amp;quot;&lt;br /&gt;
* Paragraph 2: &amp;quot;It operates over [transport] at [frequency] and is supported by [device list]...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Optional sub-sections:&#039;&#039;&#039;&lt;br /&gt;
* === Protocol Stack === — layered diagram of protocol stack&lt;br /&gt;
* === Roles === — Broadcaster / Observer / Central / Peripheral&lt;br /&gt;
* === Security === — encryption, authentication, password protection&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Advertising Message Format ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Byte-by-byte breakdown of the BLE advertising packet payload.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Packet overview&#039;&#039;&#039; — total packet length, manufacturer ID, structure summary&lt;br /&gt;
* &#039;&#039;&#039;Field table&#039;&#039;&#039; — every field with offset, length, value, description&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example field breakdown table:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset !! Length (bytes) !! Field Name !! Value / Format !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x00 || 1 || AD Length || 0xXX || Length of the advertising data structure&lt;br /&gt;
|-&lt;br /&gt;
| 0x01 || 1 || AD Type || 0xFF || Manufacturer Specific Data (per Bluetooth Core spec)&lt;br /&gt;
|-&lt;br /&gt;
| 0x02 || 2 || Company ID || 0xXXXX (LE) || SOJI Electronics SIG-assigned Company ID&lt;br /&gt;
|-&lt;br /&gt;
| 0x04 || 1 || Protocol ID || 0xXX || SOJI Protocol identifier&lt;br /&gt;
|-&lt;br /&gt;
| 0x05 || 1 || Frame Type || 0xXX || Frame type (data frame, status frame, ...)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06 || N || Payload || ... || Sensor data payload (see Payload Structure below)&lt;br /&gt;
|-&lt;br /&gt;
| 0x06+N || 2 || CRC-16 || 0xXXXX || CRC-16/CCITT checksum over Offset 0x00 to 0x05+N&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sub-sections to consider:&#039;&#039;&#039;&lt;br /&gt;
* === Packet Overview === — total length, byte order (LE/BE), encoding&lt;br /&gt;
* === Field Definitions === — main field table above&lt;br /&gt;
* === Payload Structure === — detailed payload fields (level, temperature, battery, RSSI, etc.) with units and scaling&lt;br /&gt;
* === Frame Types === — enumeration of frame types if multiple exist&lt;br /&gt;
* === CRC / Checksum === — algorithm (CRC-16/CCITT, polynomial, init value, XOR-out)&lt;br /&gt;
* === Example Raw Frame === — hex dump of a real advertising packet with field annotations&lt;br /&gt;
* === Decoding Pseudocode === — short code snippet (C / Python) showing how to parse a frame&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example raw frame block:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Raw bytes (hex):&lt;br /&gt;
  0E FF 5A A5 01 02 03 04 ...&lt;br /&gt;
&lt;br /&gt;
Decoded:&lt;br /&gt;
  AD Length    = 0x0E (14 bytes)&lt;br /&gt;
  AD Type      = 0xFF (Manufacturer Specific)&lt;br /&gt;
  Company ID   = 0xA55A (SOJI Electronics)&lt;br /&gt;
  Protocol ID  = 0x01 (SOJI Protocol)&lt;br /&gt;
  Frame Type   = 0x02 (Data Frame)&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example decoding pseudocode:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
def decode_soji_frame(adv_data: bytes) -&amp;gt; dict:&lt;br /&gt;
    if adv_data[1] != 0xFF:&lt;br /&gt;
        return None&lt;br /&gt;
    company_id = int.from_bytes(adv_data[2:4], &#039;little&#039;)&lt;br /&gt;
    protocol_id = adv_data[4]&lt;br /&gt;
    frame_type = adv_data[5]&lt;br /&gt;
    payload = adv_data[6:-2]&lt;br /&gt;
    crc = int.from_bytes(adv_data[-2:], &#039;little&#039;)&lt;br /&gt;
    # ... validate CRC, parse payload&lt;br /&gt;
    return { ... }&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2 ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=MediaWiki:Common.js&amp;diff=2054</id>
		<title>MediaWiki:Common.js</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=MediaWiki:Common.js&amp;diff=2054"/>
		<updated>2026-06-24T10:51:32Z</updated>

		<summary type="html">&lt;p&gt;Admin: Update&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;(function () {&lt;br /&gt;
    function esc(text) {&lt;br /&gt;
        return String(text || &amp;quot;&amp;quot;).replace(/[&amp;amp;&amp;lt;&amp;gt;&amp;quot;]/g, function (m) {&lt;br /&gt;
            return { &amp;quot;&amp;amp;&amp;quot;: &amp;quot;&amp;amp;amp;&amp;quot;, &amp;quot;&amp;lt;&amp;quot;: &amp;quot;&amp;amp;lt;&amp;quot;, &amp;quot;&amp;gt;&amp;quot;: &amp;quot;&amp;amp;gt;&amp;quot;, &#039;&amp;quot;&#039;: &amp;quot;&amp;amp;quot;&amp;quot; }[m];&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    var ICONS = {&lt;br /&gt;
        location: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 010-5 2.5 2.5 0 010 5z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        phone: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M12 1a9 9 0 00-9 9v7a3 3 0 003 3h3v-8H5v-2a7 7 0 0114 0v2h-4v8h3a3 3 0 003-3v-7a9 9 0 00-9-9z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        email: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;none&amp;quot; stroke=&amp;quot;currentColor&amp;quot; stroke-width=&amp;quot;2&amp;quot; stroke-linecap=&amp;quot;round&amp;quot; stroke-linejoin=&amp;quot;round&amp;quot;&amp;gt;&amp;lt;circle cx=&amp;quot;12&amp;quot; cy=&amp;quot;12&amp;quot; r=&amp;quot;4&amp;quot;/&amp;gt;&amp;lt;path d=&amp;quot;M16 8v5a3 3 0 006 0v-1a10 10 0 10-4 8&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        clock: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;none&amp;quot; stroke=&amp;quot;currentColor&amp;quot; stroke-width=&amp;quot;2&amp;quot; stroke-linecap=&amp;quot;round&amp;quot; stroke-linejoin=&amp;quot;round&amp;quot;&amp;gt;&amp;lt;circle cx=&amp;quot;12&amp;quot; cy=&amp;quot;12&amp;quot; r=&amp;quot;10&amp;quot;/&amp;gt;&amp;lt;polyline points=&amp;quot;12 6 12 12 16 14&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        facebook: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M22 12a10 10 0 10-11.56 9.88v-6.99H7.9V12h2.54V9.8c0-2.51 1.49-3.89 3.77-3.89 1.09 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56V12h2.77l-.44 2.89h-2.33v6.99A10 10 0 0022 12z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        whatsapp: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M17.5 14.4c-.3-.1-1.7-.8-1.9-.9-.3-.1-.5-.1-.7.1-.2.3-.7.9-.9 1.1-.2.2-.3.2-.6.1-.3-.1-1.2-.4-2.3-1.4-.9-.8-1.4-1.7-1.6-2-.2-.3 0-.5.1-.6.1-.1.3-.3.4-.5.1-.2.2-.3.3-.5.1-.2 0-.4 0-.5 0-.1-.7-1.7-1-2.3-.3-.6-.5-.5-.7-.5h-.6c-.2 0-.5.1-.8.4-.3.3-1 1-1 2.5s1.1 2.9 1.2 3.1c.1.2 2.1 3.2 5.1 4.5.7.3 1.3.5 1.7.6.7.2 1.4.2 1.9.1.6-.1 1.7-.7 2-1.4.2-.7.2-1.3.2-1.4-.1-.1-.3-.2-.5-.3zM12 2a10 10 0 00-8.5 15.3L2 22l4.8-1.4A10 10 0 1012 2z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        linkedin: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M19 3H5a2 2 0 00-2 2v14a2 2 0 002 2h14a2 2 0 002-2V5a2 2 0 00-2-2zM8.3 18.3H5.7V9.7h2.6v8.6zM7 8.5a1.5 1.5 0 110-3 1.5 1.5 0 010 3zm11.3 9.8h-2.6v-4.2c0-1 0-2.3-1.4-2.3s-1.6 1.1-1.6 2.2v4.3h-2.6V9.7h2.5v1.2a2.7 2.7 0 012.5-1.4c2.6 0 3.1 1.7 3.1 4v4.8z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;,&lt;br /&gt;
        youtube: &#039;&amp;lt;svg viewBox=&amp;quot;0 0 24 24&amp;quot; fill=&amp;quot;currentColor&amp;quot;&amp;gt;&amp;lt;path d=&amp;quot;M23.5 6.2a3 3 0 00-2.1-2.1C19.5 3.5 12 3.5 12 3.5s-7.5 0-9.4.6A3 3 0 00.5 6.2 31.3 31.3 0 000 12a31.3 31.3 0 00.5 5.8 3 3 0 002.1 2.1c1.9.6 9.4.6 9.4.6s7.5 0 9.4-.6a3 3 0 002.1-2.1A31.3 31.3 0 0024 12a31.3 31.3 0 00-.5-5.8zM9.6 15.6V8.4l6.3 3.6-6.3 3.6z&amp;quot;/&amp;gt;&amp;lt;/svg&amp;gt;&#039;&lt;br /&gt;
    };&lt;br /&gt;
&lt;br /&gt;
    function getIcon(name) {&lt;br /&gt;
        return &#039;&amp;lt;span class=&amp;quot;soji-footer-icon&amp;quot;&amp;gt;&#039; + (ICONS[name] || &amp;quot;&amp;quot;) + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function renderLink(item, extraClass) {&lt;br /&gt;
    var cls = extraClass ? &#039; class=&amp;quot;&#039; + extraClass + &#039;&amp;quot;&#039; : &amp;quot;&amp;quot;;&lt;br /&gt;
    var href = item.href ? esc(item.href) : &amp;quot;#&amp;quot;;&lt;br /&gt;
    return &#039;&amp;lt;a&#039; + cls + &#039; href=&amp;quot;&#039; + href + &#039;&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot;&amp;gt;&#039; + esc(item.label) + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    function buildCompany(company) {&lt;br /&gt;
        if (!company) return &amp;quot;&amp;quot;;&lt;br /&gt;
        var contacts = (company.contacts || []).map(function (c) {&lt;br /&gt;
            return &#039;&amp;lt;li class=&amp;quot;soji-footer-contact&amp;quot;&amp;gt;&#039; + getIcon(c.icon) +&lt;br /&gt;
                &#039;&amp;lt;span&amp;gt;&#039; + esc(c.text) + &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
        }).join(&amp;quot;&amp;quot;);&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;&amp;lt;section class=&amp;quot;soji-footer-company&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;h2 class=&amp;quot;soji-footer-company-name&amp;quot;&amp;gt;&#039; + esc(company.name) + &amp;quot;&amp;lt;/h2&amp;gt;&amp;quot;,&lt;br /&gt;
            &#039;&amp;lt;ul class=&amp;quot;soji-footer-contact-list&amp;quot;&amp;gt;&#039; + contacts + &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
        ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildColumns(columns) {&lt;br /&gt;
        return (columns || []).map(function (col) {&lt;br /&gt;
            var items = (col.items || []).map(function (item) {&lt;br /&gt;
                return &amp;quot;&amp;lt;li&amp;gt;&amp;quot; + renderLink(item, &amp;quot;soji-footer-link&amp;quot;) + &amp;quot;&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
            }).join(&amp;quot;&amp;quot;);&lt;br /&gt;
            return [&lt;br /&gt;
                &#039;&amp;lt;section class=&amp;quot;soji-footer-col&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
                &amp;quot;&amp;lt;h2&amp;gt;&amp;quot; + esc(col.title) + &amp;quot;&amp;lt;/h2&amp;gt;&amp;quot;,&lt;br /&gt;
                &amp;quot;&amp;lt;ul&amp;gt;&amp;quot; + items + &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;,&lt;br /&gt;
                &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
            ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
        }).join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildSubscribe(data) {&lt;br /&gt;
        var sub = data.subscribe || {};&lt;br /&gt;
        var social = (data.social || []).map(function (item) {&lt;br /&gt;
    var href = item.href ? esc(item.href) : &amp;quot;#&amp;quot;;&lt;br /&gt;
    var icon = item.icon&lt;br /&gt;
        ? getIcon(item.icon)&lt;br /&gt;
        : esc(item.short || item.label.charAt(0));&lt;br /&gt;
    return &#039;&amp;lt;a class=&amp;quot;soji-footer-social-item&amp;quot; href=&amp;quot;&#039; + href + &#039;&amp;quot; target=&amp;quot;_blank&amp;quot; rel=&amp;quot;noopener noreferrer&amp;quot; aria-label=&amp;quot;&#039; + esc(item.label) + &#039;&amp;quot;&amp;gt;&#039; + icon + &amp;quot;&amp;lt;/a&amp;gt;&amp;quot;;&lt;br /&gt;
}).join(&amp;quot;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;&amp;lt;section class=&amp;quot;soji-footer-subscribe-col&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &amp;quot;&amp;lt;h2&amp;gt;&amp;quot; + esc(sub.title || &amp;quot;Subscribe&amp;quot;) + &amp;quot;&amp;lt;/h2&amp;gt;&amp;quot;,&lt;br /&gt;
            &#039;&amp;lt;form class=&amp;quot;soji-footer-subscribe-form&amp;quot; action=&amp;quot;&#039; + esc(sub.action || &amp;quot;#&amp;quot;) + &#039;&amp;quot; method=&amp;quot;post&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;input type=&amp;quot;email&amp;quot; name=&amp;quot;email&amp;quot; class=&amp;quot;soji-footer-subscribe-input&amp;quot; placeholder=&amp;quot;&#039; + esc(sub.placeholder || &amp;quot;Email Address *&amp;quot;) + &#039;&amp;quot; required&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;button type=&amp;quot;submit&amp;quot; class=&amp;quot;soji-footer-subscribe-btn&amp;quot;&amp;gt;&#039; + esc(sub.buttonLabel || &amp;quot;Send&amp;quot;) + &amp;quot;&amp;lt;/button&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/form&amp;gt;&amp;quot;,&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;soji-footer-social&amp;quot;&amp;gt;&#039; + social + &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
        ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function buildFooter(data) {&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;&amp;lt;section id=&amp;quot;soji-site-footer&amp;quot; class=&amp;quot;soji-site-footer&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;soji-footer-shell&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            &#039;&amp;lt;div class=&amp;quot;soji-footer-grid&amp;quot;&amp;gt;&#039;,&lt;br /&gt;
            buildCompany(data.company),&lt;br /&gt;
            buildColumns(data.columns),&lt;br /&gt;
            buildSubscribe(data),&lt;br /&gt;
            &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
            data.copyright ? &#039;&amp;lt;div class=&amp;quot;soji-footer-copy&amp;quot;&amp;gt;&#039; + esc(data.copyright).replace(/\{\{YEAR\}\}/g, new Date().getFullYear()) + &amp;quot;&amp;lt;/div&amp;gt;&amp;quot; : &amp;quot;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;&amp;lt;/section&amp;gt;&amp;quot;&lt;br /&gt;
        ].join(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
function injectFooter(data) {&lt;br /&gt;
    if (document.getElementById(&amp;quot;soji-site-footer&amp;quot;)) return;&lt;br /&gt;
    document.body.insertAdjacentHTML(&amp;quot;beforeend&amp;quot;, buildFooter(data));&lt;br /&gt;
    document.body.classList.add(&amp;quot;has-soji-footer&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    function init() {&lt;br /&gt;
        fetch(&amp;quot;/index.php?title=MediaWiki:FooterData.json&amp;amp;action=raw&amp;quot;)&lt;br /&gt;
            .then(function (r) { return r.text(); })&lt;br /&gt;
            .then(function (text) { return JSON.parse(text); })&lt;br /&gt;
            .then(injectFooter)&lt;br /&gt;
            .catch(function (err) { console.error(&amp;quot;Footer load failed:&amp;quot;, err); });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &amp;quot;loading&amp;quot;) {&lt;br /&gt;
        document.addEventListener(&amp;quot;DOMContentLoaded&amp;quot;, init);&lt;br /&gt;
    } else {&lt;br /&gt;
        init();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    if (mw.config.get(&#039;wgNamespaceNumber&#039;) &amp;lt; 0) return;&lt;br /&gt;
&lt;br /&gt;
    var title = mw.config.get(&#039;wgTitle&#039;);&lt;br /&gt;
    var pageName = mw.config.get(&#039;wgPageName&#039;).replace(/_/g, &#039; &#039;);&lt;br /&gt;
&lt;br /&gt;
    // Page Main Page: không cần breadcrumb&lt;br /&gt;
    if (pageName === &#039;Main Page&#039;) return;&lt;br /&gt;
&lt;br /&gt;
    // Lấy category trực tiếp của page hoặc của category page hiện tại&lt;br /&gt;
    var initialCats = mw.config.get(&#039;wgCategories&#039;) || [];&lt;br /&gt;
    if (!initialCats.length) return;&lt;br /&gt;
&lt;br /&gt;
    var api = new mw.Api();&lt;br /&gt;
    var chain = [];&lt;br /&gt;
    var seen = {};&lt;br /&gt;
&lt;br /&gt;
    // Đi ngược lên cây qua API&lt;br /&gt;
    function walkUp(catName) {&lt;br /&gt;
        if (!catName || seen[catName] || catName === &#039;Main Page&#039;) {&lt;br /&gt;
            return $.Deferred().resolve();&lt;br /&gt;
        }&lt;br /&gt;
        seen[catName] = true;&lt;br /&gt;
        chain.unshift(catName);&lt;br /&gt;
&lt;br /&gt;
        return api.get({&lt;br /&gt;
            action: &#039;query&#039;,&lt;br /&gt;
            titles: &#039;Category:&#039; + catName,&lt;br /&gt;
            prop: &#039;categories&#039;,&lt;br /&gt;
            cllimit: 1,&lt;br /&gt;
            format: &#039;json&#039;,&lt;br /&gt;
            formatversion: 2&lt;br /&gt;
        }).then(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.pages) || [];&lt;br /&gt;
            if (!pages.length || !pages[0].categories) return;&lt;br /&gt;
            var parent = pages[0].categories[0].title.replace(/^Category:/, &#039;&#039;);&lt;br /&gt;
            return walkUp(parent);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    walkUp(initialCats[0]).then(function () {&lt;br /&gt;
        var parts = [&lt;br /&gt;
            &#039;&amp;lt;a href=&amp;quot;&#039; + mw.util.getUrl(&#039;Main Page&#039;) + &#039;&amp;quot;&amp;gt;Main Page&amp;lt;/a&amp;gt;&#039;&lt;br /&gt;
        ];&lt;br /&gt;
        chain.forEach(function (c) {&lt;br /&gt;
            parts.push(&#039;&amp;lt;a href=&amp;quot;&#039; + mw.util.getUrl(c) + &#039;&amp;quot;&amp;gt;&#039; + c + &#039;&amp;lt;/a&amp;gt;&#039;);&lt;br /&gt;
        });&lt;br /&gt;
        parts.push(&#039;&amp;lt;strong&amp;gt;&#039; + title + &#039;&amp;lt;/strong&amp;gt;&#039;);&lt;br /&gt;
&lt;br /&gt;
        var html = &#039;&amp;lt;nav class=&amp;quot;custom-breadcrumb&amp;quot;&amp;gt;&#039; + parts.join(&#039; &amp;amp;gt; &#039;) + &#039;&amp;lt;/nav&amp;gt;&#039;;&lt;br /&gt;
        $(&#039;#mw-content-text&#039;).before(html);&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Tự động thêm target=&amp;quot;_blank&amp;quot; cho mọi link tới file .pdf&lt;br /&gt;
    $(&#039;a[href$=&amp;quot;.pdf&amp;quot;], a[href*=&amp;quot;.pdf?&amp;quot;], a[href*=&amp;quot;/file/&amp;quot;][href*=&amp;quot;.pdf&amp;quot;]&#039;).each(function () {&lt;br /&gt;
        $(this).attr(&#039;target&#039;, &#039;_blank&#039;);&lt;br /&gt;
        $(this).attr(&#039;rel&#039;, &#039;noopener noreferrer&#039;);&lt;br /&gt;
        // Thêm icon PDF nhỏ&lt;br /&gt;
        if (!$(this).find(&#039;.pdf-icon&#039;).length) {&lt;br /&gt;
            $(this).append(&#039; &amp;lt;span class=&amp;quot;pdf-icon&amp;quot; title=&amp;quot;Open PDF in new tab&amp;quot;&amp;gt;📄&amp;lt;/span&amp;gt;&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Force &amp;quot;View&amp;quot; links to open in new tab&lt;br /&gt;
    $(&#039;.doc-view a&#039;).attr({&lt;br /&gt;
        target: &#039;_blank&#039;,&lt;br /&gt;
        rel: &#039;noopener noreferrer&#039;&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    // Force &amp;quot;Download&amp;quot; links to trigger file download&lt;br /&gt;
    $(&#039;.doc-download a&#039;).each(function () {&lt;br /&gt;
        var $a = $(this);&lt;br /&gt;
        var href = $a.attr(&#039;href&#039;);&lt;br /&gt;
        // Extract filename from URL&lt;br /&gt;
        var match = href.match(/\/([^\/]+\.(?:pdf|zip|doc|docx|xls|xlsx))$/i);&lt;br /&gt;
        if (match) {&lt;br /&gt;
            $a.attr(&#039;download&#039;, match[1]);&lt;br /&gt;
        } else {&lt;br /&gt;
            $a.attr(&#039;download&#039;, &#039;&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================&lt;br /&gt;
 * Auto-collapse TOC sub-headings on page load&lt;br /&gt;
 * Only show H2 (main Heading) by default&lt;br /&gt;
 * ============================================ */&lt;br /&gt;
$(function () {&lt;br /&gt;
    var attempts = 0;&lt;br /&gt;
    var maxAttempts = 15;&lt;br /&gt;
&lt;br /&gt;
    function collapseAllTOC() {&lt;br /&gt;
        // Find all expanded toggle buttons in TOC&lt;br /&gt;
        var $toggles = $(&#039;.vector-toc-toggle[aria-expanded=&amp;quot;true&amp;quot;]&#039;);&lt;br /&gt;
        &lt;br /&gt;
        if ($toggles.length &amp;gt; 0) {&lt;br /&gt;
            $toggles.each(function () {&lt;br /&gt;
                // Click toggle to collapse (triggers Vue.js handler)&lt;br /&gt;
                this.click();&lt;br /&gt;
            });&lt;br /&gt;
        } else if (attempts &amp;lt; maxAttempts) {&lt;br /&gt;
            // TOC chưa render xong, retry&lt;br /&gt;
            attempts++;&lt;br /&gt;
            setTimeout(collapseAllTOC, 100);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    collapseAllTOC();&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================&lt;br /&gt;
 * Sync chevron arrow with collapsible state&lt;br /&gt;
 * ============================================ */&lt;br /&gt;
$(function () {&lt;br /&gt;
    $(&#039;.category-block-toggle span[class*=&amp;quot;mw-customtoggle-cb-&amp;quot;]&#039;).each(function () {&lt;br /&gt;
        var $toggle = $(this);&lt;br /&gt;
        var match = $toggle.attr(&#039;class&#039;).match(/mw-customtoggle-(\S+)/);&lt;br /&gt;
        if (!match) return;&lt;br /&gt;
&lt;br /&gt;
        var $target = $(&#039;#mw-customcollapsible-&#039; + match[1]);&lt;br /&gt;
        if (!$target.length) return;&lt;br /&gt;
&lt;br /&gt;
        function syncArrow() {&lt;br /&gt;
            if ($target.hasClass(&#039;mw-collapsed&#039;)) {&lt;br /&gt;
                $toggle.removeClass(&#039;is-expanded&#039;);&lt;br /&gt;
            } else {&lt;br /&gt;
                $toggle.addClass(&#039;is-expanded&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Initial state&lt;br /&gt;
        syncArrow();&lt;br /&gt;
&lt;br /&gt;
        // Update on click&lt;br /&gt;
        $toggle.on(&#039;click&#039;, function () {&lt;br /&gt;
            setTimeout(syncArrow, 50);&lt;br /&gt;
        });&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================&lt;br /&gt;
 * Pending Changes Badge for Reviewers&lt;br /&gt;
 * Show notification of pending changes count&lt;br /&gt;
 * ============================================ */&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Chỉ show cho user có quyền review (reviewer, sysop, admin)&lt;br /&gt;
    var userGroups = mw.config.get(&#039;wgUserGroups&#039;) || [];&lt;br /&gt;
    var hasReviewRights = userGroups.some(function (g) {&lt;br /&gt;
        return [&#039;reviewer&#039;, &#039;sysop&#039;, &#039;bureaucrat&#039;].indexOf(g) !== -1;&lt;br /&gt;
    });&lt;br /&gt;
&lt;br /&gt;
    if (!hasReviewRights) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Fetch pending count from API&lt;br /&gt;
    function fetchPendingCount() {&lt;br /&gt;
        new mw.Api().get({&lt;br /&gt;
            action: &#039;query&#039;,&lt;br /&gt;
            list: &#039;oldreviewedpages&#039;,&lt;br /&gt;
            ornamespace: &#039;0|14&#039;,&lt;br /&gt;
            orfilterredir: &#039;nonredirects&#039;,&lt;br /&gt;
            orlimit: 500,&lt;br /&gt;
            format: &#039;json&#039;&lt;br /&gt;
        }).done(function (data) {&lt;br /&gt;
            var pages = (data.query &amp;amp;&amp;amp; data.query.oldreviewedpages) || [];&lt;br /&gt;
            updateBadge(pages.length);&lt;br /&gt;
        }).fail(function (err) {&lt;br /&gt;
            console.log(&#039;Failed to fetch pending changes:&#039;, err);&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Update or create badge in sidebar&lt;br /&gt;
    function updateBadge(count) {&lt;br /&gt;
        var $badge = $(&#039;#pending-reviews-badge&#039;);&lt;br /&gt;
&lt;br /&gt;
        if (!$badge.length) {&lt;br /&gt;
            // Tạo badge mới — tìm sidebar và prepend&lt;br /&gt;
            var badgeHtml =&lt;br /&gt;
                &#039;&amp;lt;div id=&amp;quot;pending-reviews-badge&amp;quot; style=&amp;quot;&#039; +&lt;br /&gt;
                &#039;padding: 12px 16px;&#039; +&lt;br /&gt;
                &#039;margin: 12px 8px;&#039; +&lt;br /&gt;
                &#039;background: #fff3e0;&#039; +&lt;br /&gt;
                &#039;border: 1px solid #f57c00;&#039; +&lt;br /&gt;
                &#039;border-left: 4px solid #e65100;&#039; +&lt;br /&gt;
                &#039;border-radius: 4px;&#039; +&lt;br /&gt;
                &#039;cursor: pointer;&#039; +&lt;br /&gt;
                &#039;transition: background 0.2s;&#039; +&lt;br /&gt;
                &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;a href=&amp;quot;&#039; + mw.util.getUrl(&#039;Special:PendingChanges&#039;) + &#039;&amp;quot; style=&amp;quot;&#039; +&lt;br /&gt;
                &#039;color: #e65100;&#039; +&lt;br /&gt;
                &#039;text-decoration: none;&#039; +&lt;br /&gt;
                &#039;font-weight: 600;&#039; +&lt;br /&gt;
                &#039;font-size: 0.95em;&#039; +&lt;br /&gt;
                &#039;display: block;&#039; +&lt;br /&gt;
                &#039;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;⚠️ &amp;lt;span id=&amp;quot;pending-count&amp;quot; style=&amp;quot;&#039; +&lt;br /&gt;
                &#039;display: inline-block;&#039; +&lt;br /&gt;
                &#039;background: #e65100;&#039; +&lt;br /&gt;
                &#039;color: white;&#039; +&lt;br /&gt;
                &#039;padding: 2px 10px;&#039; +&lt;br /&gt;
                &#039;border-radius: 12px;&#039; +&lt;br /&gt;
                &#039;margin: 0 6px;&#039; +&lt;br /&gt;
                &#039;font-weight: 700;&#039; +&lt;br /&gt;
                &#039;&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt;&#039; +&lt;br /&gt;
                &#039;pending review&#039; +&lt;br /&gt;
                &#039;&amp;lt;/a&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
            // Try multiple sidebar selectors (Vector 2022 + legacy)&lt;br /&gt;
            var $sidebar = $(&#039;#mw-panel .vector-main-menu-content&#039;)&lt;br /&gt;
                .add(&#039;#mw-panel-toc-list&#039;)&lt;br /&gt;
                .add(&#039;#mw-panel&#039;)&lt;br /&gt;
                .add(&#039;.vector-main-menu&#039;)&lt;br /&gt;
                .first();&lt;br /&gt;
&lt;br /&gt;
            if ($sidebar.length) {&lt;br /&gt;
                $sidebar.prepend(badgeHtml);&lt;br /&gt;
                $badge = $(&#039;#pending-reviews-badge&#039;);&lt;br /&gt;
            } else {&lt;br /&gt;
                // Fallback: insert at top of body&lt;br /&gt;
                $(&#039;body&#039;).prepend(&lt;br /&gt;
                    &#039;&amp;lt;div style=&amp;quot;position:fixed;top:60px;right:20px;z-index:9999;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                    badgeHtml + &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                );&lt;br /&gt;
                $badge = $(&#039;#pending-reviews-badge&#039;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $(&#039;#pending-count&#039;).text(count);&lt;br /&gt;
&lt;br /&gt;
        if (count === 0) {&lt;br /&gt;
            $badge.hide();&lt;br /&gt;
        } else {&lt;br /&gt;
            $badge.show();&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Initial fetch&lt;br /&gt;
    fetchPendingCount();&lt;br /&gt;
&lt;br /&gt;
    // Refresh every 60 seconds&lt;br /&gt;
    setInterval(fetchPendingCount, 60000);&lt;br /&gt;
&lt;br /&gt;
    // Also refresh on focus (when user comes back to tab)&lt;br /&gt;
    $(window).on(&#039;focus&#039;, fetchPendingCount);&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Placeholder auto-hide&lt;br /&gt;
 *  Logic: section nào có user content → ẩn placeholder trong section đó&lt;br /&gt;
 *         section trống chỉ có placeholder → vẫn hiển thị (làm hint)&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function hidePlaceholdersInFilledSections() {&lt;br /&gt;
        var $headings = $( &#039;.mw-parser-output h1, .mw-parser-output h2&#039; );&lt;br /&gt;
        if ( !$headings.length ) return;&lt;br /&gt;
&lt;br /&gt;
        $headings.each( function ( i ) {&lt;br /&gt;
            var $h = $( this );&lt;br /&gt;
            var $nextH = $headings.eq( i + 1 );&lt;br /&gt;
            var $sectionEls = $nextH.length ? $h.nextUntil( $nextH ) : $h.nextAll();&lt;br /&gt;
&lt;br /&gt;
            // Đọc text trong section, loại trừ text của placeholder&lt;br /&gt;
            var contentText = &#039;&#039;;&lt;br /&gt;
            $sectionEls.each( function () {&lt;br /&gt;
                var $clone = $( this ).clone();&lt;br /&gt;
                $clone.find( &#039;.soji-placeholder&#039; ).remove();&lt;br /&gt;
                contentText += $clone.text();&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
            // Nếu section có user content (ngoài placeholder) → ẩn placeholder&lt;br /&gt;
            if ( contentText.trim().length &amp;gt; 0 ) {&lt;br /&gt;
                $sectionEls.find( &#039;.soji-placeholder&#039; ).hide();&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // Xử lý riêng placeholder trong table cell:&lt;br /&gt;
        // Nếu cell có content khác → ẩn placeholder của cell đó&lt;br /&gt;
        $( &#039;.soji-placeholder:visible&#039; ).each( function () {&lt;br /&gt;
            var $ph = $( this );&lt;br /&gt;
            var $cell = $ph.closest( &#039;td, th&#039; );&lt;br /&gt;
            if ( $cell.length ) {&lt;br /&gt;
                var $clone = $cell.clone();&lt;br /&gt;
                $clone.find( &#039;.soji-placeholder&#039; ).remove();&lt;br /&gt;
                if ( $clone.text().trim().length &amp;gt; 0 ) {&lt;br /&gt;
                    $ph.hide();&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( hidePlaceholdersInFilledSections );&lt;br /&gt;
&lt;br /&gt;
})( jQuery );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Edit summary helper&lt;br /&gt;
 *  Show hint placeholder in edit summary field&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    $( function () {&lt;br /&gt;
        if ( mw.config.get( &#039;wgAction&#039; ) !== &#039;edit&#039; &amp;amp;&amp;amp; mw.config.get( &#039;wgAction&#039; ) !== &#039;submit&#039; ) return;&lt;br /&gt;
&lt;br /&gt;
        var $summary = $( &#039;#wpSummary&#039; );&lt;br /&gt;
        if ( !$summary.length ) return;&lt;br /&gt;
&lt;br /&gt;
        // Add placeholder hint&lt;br /&gt;
        $summary.attr( &#039;placeholder&#039;,&lt;br /&gt;
            &#039;Describe your changes (will appear in Revision History — e.g., &amp;quot;Updated electrical specs&amp;quot;, &amp;quot;Fixed typo in safety section&amp;quot;)&#039;&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        // Add character counter&lt;br /&gt;
        var $counter = $( &#039;&amp;lt;div class=&amp;quot;fw-summary-counter&amp;quot; style=&amp;quot;font-size:0.85em; color:#666; margin-top:4px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039; );&lt;br /&gt;
        $summary.after( $counter );&lt;br /&gt;
&lt;br /&gt;
        function updateCounter() {&lt;br /&gt;
            var len = $summary.val().length;&lt;br /&gt;
            var color = len &amp;lt; 10 ? &#039;#d33&#039; : len &amp;lt; 30 ? &#039;#f80&#039; : &#039;#0a0&#039;;&lt;br /&gt;
            $counter.html(&lt;br /&gt;
                &#039;&amp;lt;span style=&amp;quot;color:&#039; + color + &#039;&amp;quot;&amp;gt;&#039; + len + &#039;&amp;lt;/span&amp;gt; characters &#039; +&lt;br /&gt;
                ( len &amp;lt; 10 ? &#039;(too short — describe what changed)&#039; :&lt;br /&gt;
                  len &amp;lt; 30 ? &#039;(acceptable — be more specific)&#039; :&lt;br /&gt;
                  &#039;(good — descriptive)&#039; )&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $summary.on( &#039;input keyup&#039;, updateCounter );&lt;br /&gt;
        updateCounter();&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Auto Revision History Table&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function formatVersion( n ) {&lt;br /&gt;
        var major = Math.floor( ( n - 1 ) / 10 ) + 1;&lt;br /&gt;
        var minor = ( n - 1 ) % 10;&lt;br /&gt;
        return major + &#039;.&#039; + minor;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function formatDate( isoTimestamp ) {&lt;br /&gt;
        var d = new Date( isoTimestamp );&lt;br /&gt;
        var dd = ( &#039;0&#039; + d.getDate() ).slice( -2 );&lt;br /&gt;
        var mm = ( &#039;0&#039; + ( d.getMonth() + 1 ) ).slice( -2 );&lt;br /&gt;
        return dd + &#039;/&#039; + mm + &#039;/&#039; + d.getFullYear();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function escapeHtml( str ) {&lt;br /&gt;
        return ( str || &#039;&#039; )&lt;br /&gt;
            .replace( /&amp;amp;/g, &#039;&amp;amp;amp;&#039; )&lt;br /&gt;
            .replace( /&amp;lt;/g, &#039;&amp;amp;lt;&#039; )&lt;br /&gt;
            .replace( /&amp;gt;/g, &#039;&amp;amp;gt;&#039; )&lt;br /&gt;
            .replace( /&amp;quot;/g, &#039;&amp;amp;quot;&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function renderRevisionHistory() {&lt;br /&gt;
    var $containers = $( &#039;.revision-history-auto&#039; );&lt;br /&gt;
    if ( !$containers.length ) return;&lt;br /&gt;
&lt;br /&gt;
    var pageName = mw.config.get( &#039;wgPageName&#039; );&lt;br /&gt;
&lt;br /&gt;
    new mw.Api().get( {&lt;br /&gt;
        action: &#039;query&#039;,&lt;br /&gt;
        prop: &#039;revisions&#039;,&lt;br /&gt;
        titles: pageName,&lt;br /&gt;
        rvprop: &#039;timestamp|user|comment&#039;,&lt;br /&gt;
        rvlimit: &#039;max&#039;,&lt;br /&gt;
        rvdir: &#039;newer&#039;,           // oldest first&lt;br /&gt;
        formatversion: 2&lt;br /&gt;
    } ).done( function ( data ) {&lt;br /&gt;
        var page = data.query.pages[ 0 ];&lt;br /&gt;
        if ( !page || !page.revisions ) {&lt;br /&gt;
            $containers.html( &#039;&amp;lt;em&amp;gt;No revision history available.&amp;lt;/em&amp;gt;&#039; );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        var revisions = page.revisions;&lt;br /&gt;
&lt;br /&gt;
        // Case 1: chỉ có 1 revision = template gen, chưa edit&lt;br /&gt;
        if ( revisions.length &amp;lt; 2 ) {&lt;br /&gt;
            $containers.html(&lt;br /&gt;
                &#039;&amp;lt;em style=&amp;quot;color:#888&amp;quot;&amp;gt;No edits yet. &#039; +&lt;br /&gt;
                &#039;Edit this page to update the content — the first edit will be recorded as v1.0.&amp;lt;/em&amp;gt;&#039;&lt;br /&gt;
            );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Case 2: có &amp;gt;= 2 revisions&lt;br /&gt;
        // - revision 0 = template creation → SKIP&lt;br /&gt;
        // - revision 1 = first edit → v1.0&lt;br /&gt;
        // - revision 2 = second edit → v1.1&lt;br /&gt;
        // - ...&lt;br /&gt;
        var rows = [];&lt;br /&gt;
&lt;br /&gt;
        revisions.forEach( function ( rev, i ) {&lt;br /&gt;
            // Skip first revision (template creation)&lt;br /&gt;
            if ( i === 0 ) return;&lt;br /&gt;
&lt;br /&gt;
            // i=1 → v1.0, i=2 → v1.1, ...&lt;br /&gt;
            var version = formatVersion( i );&lt;br /&gt;
            var date    = formatDate( rev.timestamp );&lt;br /&gt;
            var author  = escapeHtml( rev.user || &#039;unknown&#039; );&lt;br /&gt;
            var desc    = escapeHtml( rev.comment ) || &#039;&amp;lt;em&amp;gt;(no edit summary)&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
            rows.push(&lt;br /&gt;
                &#039;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&#039; + date + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;td&amp;gt;&amp;lt;strong&amp;gt;v&#039; + version + &#039;&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;td&amp;gt;&#039; + author + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;td&amp;gt;&#039; + desc + &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;&lt;br /&gt;
            );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        // Newest first&lt;br /&gt;
        rows.reverse();&lt;br /&gt;
&lt;br /&gt;
        var html =&lt;br /&gt;
            &#039;&amp;lt;table class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Date&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Version&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th style=&amp;quot;width:20%&amp;quot;&amp;gt;Author&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;tbody&amp;gt;&#039; + rows.join( &#039;&#039; ) + &#039;&amp;lt;/tbody&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/table&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
        $containers.html( html );&lt;br /&gt;
    } ).fail( function ( err ) {&lt;br /&gt;
        $containers.html( &#039;&amp;lt;em style=&amp;quot;color:#d33&amp;quot;&amp;gt;Failed to load revision history.&amp;lt;/em&amp;gt;&#039; );&lt;br /&gt;
    } );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
    $( renderRevisionHistory );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Force edit summary in VisualEditor save dialog&lt;br /&gt;
 *  Disable Save button until summary is non-empty (min 5 chars)&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var MIN_LENGTH = 5;&lt;br /&gt;
&lt;br /&gt;
    function setupVEEnforcement() {&lt;br /&gt;
        // Wait for VE save dialog to appear&lt;br /&gt;
        var observer = new MutationObserver( function () {&lt;br /&gt;
            var $dialog = $( &#039;.ve-ui-mwSaveDialog:visible&#039; );&lt;br /&gt;
            if ( !$dialog.length ) return;&lt;br /&gt;
            if ( $dialog.data( &#039;summary-enforced&#039; ) ) return;&lt;br /&gt;
&lt;br /&gt;
            $dialog.data( &#039;summary-enforced&#039;, true );&lt;br /&gt;
&lt;br /&gt;
            var $summaryInput = $dialog.find( &#039;textarea[name=&amp;quot;wpSummary&amp;quot;], input[name=&amp;quot;wpSummary&amp;quot;]&#039; );&lt;br /&gt;
            if ( !$summaryInput.length ) {&lt;br /&gt;
                $summaryInput = $dialog.find( &#039;.ve-ui-mwSaveDialog-summary textarea, .ve-ui-mwSaveDialog-summary input&#039; );&lt;br /&gt;
            }&lt;br /&gt;
            if ( !$summaryInput.length ) return;&lt;br /&gt;
&lt;br /&gt;
            var $saveBtn = $dialog.find( &#039;.oo-ui-flaggedElement-primary button, button.oo-ui-flaggedElement-primary&#039; );&lt;br /&gt;
            if ( !$saveBtn.length ) {&lt;br /&gt;
                $saveBtn = $dialog.find( &#039;a.oo-ui-flaggedElement-primary&#039; );&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Add hint message above summary&lt;br /&gt;
            if ( !$dialog.find( &#039;.soji-summary-hint&#039; ).length ) {&lt;br /&gt;
                $summaryInput.before(&lt;br /&gt;
                    &#039;&amp;lt;div class=&amp;quot;soji-summary-hint&amp;quot; style=&amp;quot;background:#fff3cd; color:#856404; padding:8px 12px; border-left:4px solid #ffc107; margin-bottom:8px; font-size:0.9em; border-radius:3px;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                    &#039;⚠ &amp;lt;strong&amp;gt;Edit summary required.&amp;lt;/strong&amp;gt; Describe what you changed (minimum &#039; + MIN_LENGTH + &#039; characters). Used for Revision History.&#039; +&lt;br /&gt;
                    &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                );&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Add counter&lt;br /&gt;
            if ( !$dialog.find( &#039;.soji-summary-counter&#039; ).length ) {&lt;br /&gt;
                $summaryInput.after(&lt;br /&gt;
                    &#039;&amp;lt;div class=&amp;quot;soji-summary-counter&amp;quot; style=&amp;quot;font-size:0.85em; margin-top:4px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
                );&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            function updateState() {&lt;br /&gt;
                var len = $summaryInput.val().trim().length;&lt;br /&gt;
                var $counter = $dialog.find( &#039;.soji-summary-counter&#039; );&lt;br /&gt;
&lt;br /&gt;
                if ( len === 0 ) {&lt;br /&gt;
                    $counter.html( &#039;&amp;lt;span style=&amp;quot;color:#d33&amp;quot;&amp;gt;⚠ Empty — please describe your changes&amp;lt;/span&amp;gt;&#039; );&lt;br /&gt;
                    disableSaveButton( $saveBtn );&lt;br /&gt;
                } else if ( len &amp;lt; MIN_LENGTH ) {&lt;br /&gt;
                    $counter.html( &#039;&amp;lt;span style=&amp;quot;color:#f80&amp;quot;&amp;gt;⚠ Too short (&#039; + len + &#039;/&#039; + MIN_LENGTH + &#039; chars minimum)&amp;lt;/span&amp;gt;&#039; );&lt;br /&gt;
                    disableSaveButton( $saveBtn );&lt;br /&gt;
                } else {&lt;br /&gt;
                    $counter.html( &#039;&amp;lt;span style=&amp;quot;color:#0a0&amp;quot;&amp;gt;✓ &#039; + len + &#039; characters — good&amp;lt;/span&amp;gt;&#039; );&lt;br /&gt;
                    enableSaveButton( $saveBtn );&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            $summaryInput.on( &#039;input keyup change&#039;, updateState );&lt;br /&gt;
            updateState();&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        observer.observe( document.body, { childList: true, subtree: true } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function disableSaveButton( $btn ) {&lt;br /&gt;
        $btn.css( &#039;opacity&#039;, &#039;0.5&#039; ).css( &#039;pointer-events&#039;, &#039;none&#039; );&lt;br /&gt;
        $btn.attr( &#039;aria-disabled&#039;, &#039;true&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function enableSaveButton( $btn ) {&lt;br /&gt;
        $btn.css( &#039;opacity&#039;, &#039;&#039; ).css( &#039;pointer-events&#039;, &#039;&#039; );&lt;br /&gt;
        $btn.removeAttr( &#039;aria-disabled&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( setupVEEnforcement );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Edit Draft Auto-Save (localStorage)&lt;br /&gt;
 *  Save draft mỗi 30s, restore khi mở lại edit&lt;br /&gt;
 *  Backup cho trường hợp stash expire hoặc browser crash&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var SAVE_INTERVAL = 30 * 1000;  // 30 seconds&lt;br /&gt;
    var DRAFT_TTL    = 30 * 24 * 60 * 60 * 1000;  // 30 days&lt;br /&gt;
    var STORAGE_PREFIX = &#039;mw-draft-&#039;;&lt;br /&gt;
&lt;br /&gt;
    function getDraftKey() {&lt;br /&gt;
        return STORAGE_PREFIX + mw.config.get( &#039;wgPageName&#039; );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function saveDraft( content ) {&lt;br /&gt;
        try {&lt;br /&gt;
            var data = {&lt;br /&gt;
                content: content,&lt;br /&gt;
                timestamp: Date.now(),&lt;br /&gt;
                page: mw.config.get( &#039;wgPageName&#039; ),&lt;br /&gt;
                user: mw.config.get( &#039;wgUserName&#039; )&lt;br /&gt;
            };&lt;br /&gt;
            localStorage.setItem( getDraftKey(), JSON.stringify( data ) );&lt;br /&gt;
            return true;&lt;br /&gt;
        } catch ( e ) {&lt;br /&gt;
            console.warn( &#039;Failed to save draft:&#039;, e );&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function loadDraft() {&lt;br /&gt;
        try {&lt;br /&gt;
            var raw = localStorage.getItem( getDraftKey() );&lt;br /&gt;
            if ( !raw ) return null;&lt;br /&gt;
            var data = JSON.parse( raw );&lt;br /&gt;
            if ( Date.now() - data.timestamp &amp;gt; DRAFT_TTL ) {&lt;br /&gt;
                localStorage.removeItem( getDraftKey() );&lt;br /&gt;
                return null;&lt;br /&gt;
            }&lt;br /&gt;
            return data;&lt;br /&gt;
        } catch ( e ) {&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function clearDraft() {&lt;br /&gt;
        try {&lt;br /&gt;
            localStorage.removeItem( getDraftKey() );&lt;br /&gt;
        } catch ( e ) {}&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function formatAge( ts ) {&lt;br /&gt;
        var diff = Date.now() - ts;&lt;br /&gt;
        var mins = Math.floor( diff / 60000 );&lt;br /&gt;
        if ( mins &amp;lt; 1 ) return &#039;just now&#039;;&lt;br /&gt;
        if ( mins &amp;lt; 60 ) return mins + &#039; min ago&#039;;&lt;br /&gt;
        var hrs = Math.floor( mins / 60 );&lt;br /&gt;
        if ( hrs &amp;lt; 24 ) return hrs + &#039; hour&#039; + ( hrs &amp;gt; 1 ? &#039;s&#039; : &#039;&#039; ) + &#039; ago&#039;;&lt;br /&gt;
        var days = Math.floor( hrs / 24 );&lt;br /&gt;
        return days + &#039; day&#039; + ( days &amp;gt; 1 ? &#039;s&#039; : &#039;&#039; ) + &#039; ago&#039;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupSourceEditor() {&lt;br /&gt;
        var $textarea = $( &#039;#wpTextbox1&#039; );&lt;br /&gt;
        if ( !$textarea.length ) return;&lt;br /&gt;
&lt;br /&gt;
        // Restore draft on load&lt;br /&gt;
        var draft = loadDraft();&lt;br /&gt;
        if ( draft &amp;amp;&amp;amp; draft.content !== $textarea.val() ) {&lt;br /&gt;
            var age = formatAge( draft.timestamp );&lt;br /&gt;
            var $banner = $(&lt;br /&gt;
                &#039;&amp;lt;div class=&amp;quot;mw-draft-banner&amp;quot; style=&amp;quot;background:#fff3cd; border-left:4px solid #ffc107; padding:10px 14px; margin:8px 0; border-radius:3px;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;strong&amp;gt;📝 Unsaved draft found&amp;lt;/strong&amp;gt; (&#039; + age + &#039;)&amp;lt;br&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;mw-draft-restore&amp;quot; style=&amp;quot;margin-top:6px; margin-right:8px;&amp;quot;&amp;gt;Restore draft&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;button type=&amp;quot;button&amp;quot; class=&amp;quot;mw-draft-discard&amp;quot; style=&amp;quot;margin-top:6px;&amp;quot;&amp;gt;Discard draft&amp;lt;/button&amp;gt;&#039; +&lt;br /&gt;
                &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
            );&lt;br /&gt;
            $textarea.before( $banner );&lt;br /&gt;
&lt;br /&gt;
            $banner.find( &#039;.mw-draft-restore&#039; ).on( &#039;click&#039;, function () {&lt;br /&gt;
                $textarea.val( draft.content );&lt;br /&gt;
                $banner.remove();&lt;br /&gt;
            } );&lt;br /&gt;
            $banner.find( &#039;.mw-draft-discard&#039; ).on( &#039;click&#039;, function () {&lt;br /&gt;
                clearDraft();&lt;br /&gt;
                $banner.remove();&lt;br /&gt;
            } );&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Auto-save every 30s&lt;br /&gt;
        var saveTimer = setInterval( function () {&lt;br /&gt;
            saveDraft( $textarea.val() );&lt;br /&gt;
            showSaveIndicator();&lt;br /&gt;
        }, SAVE_INTERVAL );&lt;br /&gt;
&lt;br /&gt;
        // Save on every change (throttled)&lt;br /&gt;
        var changeTimer;&lt;br /&gt;
        $textarea.on( &#039;input&#039;, function () {&lt;br /&gt;
            clearTimeout( changeTimer );&lt;br /&gt;
            changeTimer = setTimeout( function () {&lt;br /&gt;
                saveDraft( $textarea.val() );&lt;br /&gt;
            }, 2000 );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        // Clear draft on successful save&lt;br /&gt;
        $( &#039;#editform&#039; ).on( &#039;submit&#039;, function () {&lt;br /&gt;
            // Wait a bit, then clear if save successful (page redirected)&lt;br /&gt;
            setTimeout( clearDraft, 500 );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        // Save on tab close&lt;br /&gt;
        window.addEventListener( &#039;beforeunload&#039;, function () {&lt;br /&gt;
            saveDraft( $textarea.val() );&lt;br /&gt;
        } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function setupVisualEditor() {&lt;br /&gt;
        // VE saves to its own state, but we can backup the wikitext periodically&lt;br /&gt;
        mw.hook( &#039;ve.activationComplete&#039; ).add( function () {&lt;br /&gt;
            var saveTimer = setInterval( function () {&lt;br /&gt;
                if ( !mw.libs.ve || !mw.libs.ve.targetLoader ) return;&lt;br /&gt;
                try {&lt;br /&gt;
                    var target = ve.init.target;&lt;br /&gt;
                    if ( target &amp;amp;&amp;amp; target.getSurface ) {&lt;br /&gt;
                        var surface = target.getSurface();&lt;br /&gt;
                        if ( surface ) {&lt;br /&gt;
                            var html = surface.getHtml();&lt;br /&gt;
                            saveDraft( html );&lt;br /&gt;
                            showSaveIndicator();&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                } catch ( e ) {}&lt;br /&gt;
            }, SAVE_INTERVAL );&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        mw.hook( &#039;ve.deactivationComplete&#039; ).add( function () {&lt;br /&gt;
            // Don&#039;t clear automatically — user may want to reopen&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        mw.hook( &#039;ve.saveComplete&#039; ).add( function () {&lt;br /&gt;
            clearDraft();&lt;br /&gt;
        } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function showSaveIndicator() {&lt;br /&gt;
        var $existing = $( &#039;#mw-draft-indicator&#039; );&lt;br /&gt;
        if ( $existing.length ) {&lt;br /&gt;
            $existing.show().delay( 2000 ).fadeOut( 500 );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        var $indicator = $(&lt;br /&gt;
            &#039;&amp;lt;div id=&amp;quot;mw-draft-indicator&amp;quot; style=&amp;quot;position:fixed; bottom:20px; right:20px; background:#0a0; color:white; padding:6px 12px; border-radius:4px; font-size:0.85em; z-index:9999; box-shadow:0 2px 8px rgba(0,0,0,0.2);&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;💾 Draft saved locally&#039; +&lt;br /&gt;
            &#039;&amp;lt;/div&amp;gt;&#039;&lt;br /&gt;
        );&lt;br /&gt;
        $( &#039;body&#039; ).append( $indicator );&lt;br /&gt;
        $indicator.delay( 2000 ).fadeOut( 500, function () { $( this ).remove(); } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( function () {&lt;br /&gt;
        var action = mw.config.get( &#039;wgAction&#039; );&lt;br /&gt;
        if ( action === &#039;edit&#039; || action === &#039;submit&#039; ) {&lt;br /&gt;
            setupSourceEditor();&lt;br /&gt;
        }&lt;br /&gt;
        if ( mw.config.get( &#039;wgVisualEditor&#039; ) ) {&lt;br /&gt;
            setupVisualEditor();&lt;br /&gt;
        }&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  MultiBoilerplate auto-load on dropdown change&lt;br /&gt;
 *  Use URL navigation instead of form submission (more reliable)&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $, mw ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    $( function () {&lt;br /&gt;
        // Only run on edit action&lt;br /&gt;
        if ( mw.config.get( &#039;wgAction&#039; ) !== &#039;edit&#039; ) return;&lt;br /&gt;
&lt;br /&gt;
        // Find MultiBoilerplate dropdown — try multiple selectors&lt;br /&gt;
        var $select = $( &#039;select[name=&amp;quot;boilerplate&amp;quot;], #multiboilerplate-select, #boilerplate-select&#039; );&lt;br /&gt;
&lt;br /&gt;
        if ( !$select.length ) {&lt;br /&gt;
            console.log( &#039;[MB-AutoLoad] No boilerplate dropdown found on this page&#039; );&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        console.log( &#039;[MB-AutoLoad] Found dropdown:&#039;, $select[ 0 ] );&lt;br /&gt;
&lt;br /&gt;
        // Find Load button to hide it&lt;br /&gt;
        var $form = $select.closest( &#039;form&#039; );&lt;br /&gt;
        var $submitBtn = $form.find( &#039;input[type=&amp;quot;submit&amp;quot;], button[type=&amp;quot;submit&amp;quot;]&#039; ).filter( function () {&lt;br /&gt;
            var text = ( $( this ).val() || $( this ).text() || &#039;&#039; ).toLowerCase();&lt;br /&gt;
            return text.indexOf( &#039;load&#039; ) !== -1 || text.indexOf( &#039;boilerplate&#039; ) !== -1;&lt;br /&gt;
        } );&lt;br /&gt;
&lt;br /&gt;
        $submitBtn.hide();&lt;br /&gt;
&lt;br /&gt;
        // Add loading indicator&lt;br /&gt;
        var $indicator = $(&lt;br /&gt;
            &#039;&amp;lt;span class=&amp;quot;mb-loading-indicator&amp;quot; style=&amp;quot;margin-left:8px; color:#0054A6; font-style:italic; display:none;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;⏳ Loading template...&#039; +&lt;br /&gt;
            &#039;&amp;lt;/span&amp;gt;&#039;&lt;br /&gt;
        );&lt;br /&gt;
        $select.after( $indicator );&lt;br /&gt;
&lt;br /&gt;
        // Auto-load on dropdown change&lt;br /&gt;
        $select.on( &#039;change&#039;, function () {&lt;br /&gt;
            var value = $( this ).val();&lt;br /&gt;
&lt;br /&gt;
            console.log( &#039;[MB-AutoLoad] Selected:&#039;, value );&lt;br /&gt;
&lt;br /&gt;
            if ( !value || value === &#039;&#039; || value === &#039;-&#039; ) {&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Show loading&lt;br /&gt;
            $indicator.show();&lt;br /&gt;
            $select.prop( &#039;disabled&#039;, true );&lt;br /&gt;
&lt;br /&gt;
            // Build URL with boilerplate parameter&lt;br /&gt;
            var url = new URL( window.location.href );&lt;br /&gt;
            url.searchParams.set( &#039;boilerplate&#039;, value );&lt;br /&gt;
&lt;br /&gt;
            console.log( &#039;[MB-AutoLoad] Navigating to:&#039;, url.toString() );&lt;br /&gt;
&lt;br /&gt;
            // Small delay for visual feedback&lt;br /&gt;
            setTimeout( function () {&lt;br /&gt;
                window.location.href = url.toString();&lt;br /&gt;
            }, 300 );&lt;br /&gt;
        } );&lt;br /&gt;
    } );&lt;br /&gt;
&lt;br /&gt;
} )( jQuery, mediaWiki );&lt;br /&gt;
/* Auto-convert spaces to dashes in software-card filename hints */&lt;br /&gt;
$(function() {&lt;br /&gt;
    $(&#039;.software-card-no-icon-text code&#039;).each(function() {&lt;br /&gt;
        var text = $(this).text();&lt;br /&gt;
        // Replace spaces with dashes&lt;br /&gt;
        $(this).text(text.replace(/ /g, &#039;-&#039;));&lt;br /&gt;
    });&lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Filename hint formatter&lt;br /&gt;
 *  Convert spaces → dashes, strip special chars&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function cleanFilename( text ) {&lt;br /&gt;
        return text&lt;br /&gt;
            .replace( /&amp;amp;amp;/g, &#039;&amp;amp;&#039; )      // decode HTML entity first&lt;br /&gt;
            .replace( /&amp;amp;#38;/g, &#039;&amp;amp;&#039; )&lt;br /&gt;
            .replace( / &amp;amp; /g, &#039;-&#039; )         // &amp;quot; &amp;amp; &amp;quot; → &amp;quot;-&amp;quot;&lt;br /&gt;
            .replace( /&amp;amp;/g, &#039;&#039; )            // remove remaining &amp;amp;&lt;br /&gt;
            .replace( / /g, &#039;-&#039; )           // space → dash&lt;br /&gt;
            .replace( /-+/g, &#039;-&#039; );         // multiple dashes → single&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function formatFilenameHints() {&lt;br /&gt;
        var selectors = [&lt;br /&gt;
            &#039;.cert-card-missing code&#039;,&lt;br /&gt;
            &#039;.software-card-no-icon-text code&#039;,&lt;br /&gt;
            &#039;.accessory-card-no-icon-text code&#039;,&lt;br /&gt;
            &#039;.product-no-image-text code&#039;,&lt;br /&gt;
            &#039;.faq-card-no-icon-text code&#039;&lt;br /&gt;
        ].join( &#039;, &#039; );&lt;br /&gt;
&lt;br /&gt;
        $( selectors ).each( function () {&lt;br /&gt;
            var $code = $( this );&lt;br /&gt;
            if ( $code.data( &#039;filename-formatted&#039; ) ) return;&lt;br /&gt;
&lt;br /&gt;
            var text = $code.text();&lt;br /&gt;
            var formatted = cleanFilename( text );&lt;br /&gt;
&lt;br /&gt;
            if ( formatted !== text ) {&lt;br /&gt;
                $code.text( formatted );&lt;br /&gt;
            }&lt;br /&gt;
            $code.data( &#039;filename-formatted&#039;, &#039;1&#039; );&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $( formatFilenameHints );&lt;br /&gt;
&lt;br /&gt;
    if ( window.MutationObserver ) {&lt;br /&gt;
        var observer = new MutationObserver( function () {&lt;br /&gt;
            formatFilenameHints();&lt;br /&gt;
        });&lt;br /&gt;
        observer.observe( document.body, { childList: true, subtree: true } );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
} )( jQuery );&lt;br /&gt;
&lt;br /&gt;
/* Real-time filter for dynamically loaded sidebar items */&lt;br /&gt;
( function ( $ ) {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    var BLOCKED_PATTERNS = [&lt;br /&gt;
        &#039;Product_Change_Notifications&#039;,&lt;br /&gt;
        &#039;Product Change Notifications&#039;,&lt;br /&gt;
        &#039;Firmware_Changelog&#039;,&lt;br /&gt;
        &#039;Firmware Changelog&#039;,&lt;br /&gt;
        &#039;Promotional_Material&#039;,&lt;br /&gt;
        &#039;Promotional Material&#039;,&lt;br /&gt;
        &#039;Certifications&#039;,&lt;br /&gt;
        &#039;Certification_%26_Approvals&#039;,&lt;br /&gt;
        &#039;Certification &amp;amp; Approvals&#039;&lt;br /&gt;
    ];&lt;br /&gt;
&lt;br /&gt;
    function hideBlocked( context ) {&lt;br /&gt;
        var $context = context ? $( context ) : $( document );&lt;br /&gt;
        $context.find( &#039;#p-categorytree-portlet a, #mw-panel a&#039; ).each( function () {&lt;br /&gt;
            var $link = $( this );&lt;br /&gt;
            var href = $link.attr( &#039;href&#039; ) || &#039;&#039;;&lt;br /&gt;
            var text = $link.text();&lt;br /&gt;
&lt;br /&gt;
            for ( var i = 0; i &amp;lt; BLOCKED_PATTERNS.length; i++ ) {&lt;br /&gt;
                var pattern = BLOCKED_PATTERNS[ i ];&lt;br /&gt;
                if ( href.indexOf( pattern ) !== -1 || text.indexOf( pattern.replace( /_/g, &#039; &#039; ) ) !== -1 ) {&lt;br /&gt;
                    $link.closest( &#039;li, .CategoryTreeItem&#039; ).addClass( &#039;sidebar-hidden&#039; );&lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Run immediately&lt;br /&gt;
    hideBlocked();&lt;br /&gt;
&lt;br /&gt;
    // Observe sidebar for dynamic content&lt;br /&gt;
    if ( window.MutationObserver ) {&lt;br /&gt;
        var sidebar = document.querySelector( &#039;#p-categorytree-portlet, #mw-panel&#039; );&lt;br /&gt;
        if ( sidebar ) {&lt;br /&gt;
            new MutationObserver( function ( mutations ) {&lt;br /&gt;
                mutations.forEach( function ( m ) {&lt;br /&gt;
                    if ( m.addedNodes.length &amp;gt; 0 ) {&lt;br /&gt;
                        hideBlocked( m.target );&lt;br /&gt;
                    }&lt;br /&gt;
                });&lt;br /&gt;
            }).observe( sidebar, {&lt;br /&gt;
                childList: true,&lt;br /&gt;
                subtree: true&lt;br /&gt;
            });&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
})( jQuery );&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Auto-play video on scroll into view (IntersectionObserver)&lt;br /&gt;
 *  Play when visible, pause when scrolled out, loop infinitely&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
( function () {&lt;br /&gt;
    &#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
    function setupScrollVideos() {&lt;br /&gt;
        if ( !( &#039;IntersectionObserver&#039; in window ) ) return;&lt;br /&gt;
&lt;br /&gt;
        var videos = document.querySelectorAll( &#039;video.scroll-play, .scroll-play video&#039; );&lt;br /&gt;
        if ( !videos.length ) return;&lt;br /&gt;
&lt;br /&gt;
        var observer = new IntersectionObserver( function ( entries ) {&lt;br /&gt;
            entries.forEach( function ( entry ) {&lt;br /&gt;
                var video = entry.target;&lt;br /&gt;
                if ( entry.isIntersecting ) {&lt;br /&gt;
                    video.play().catch( function () {&lt;br /&gt;
                        // Autoplay blocked by browser, mute and try again&lt;br /&gt;
                        video.muted = true;&lt;br /&gt;
                        video.play();&lt;br /&gt;
                    });&lt;br /&gt;
                } else {&lt;br /&gt;
                    video.pause();&lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
        }, {&lt;br /&gt;
            threshold: 0.5  // play when 50% visible&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        videos.forEach( function ( video ) {&lt;br /&gt;
            video.muted = true;        // required for autoplay in most browsers&lt;br /&gt;
            video.loop = true;          // infinite loop&lt;br /&gt;
            video.playsInline = true;   // iOS Safari support&lt;br /&gt;
            observer.observe( video );&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ( document.readyState !== &#039;loading&#039; ) {&lt;br /&gt;
        setupScrollVideos();&lt;br /&gt;
    } else {&lt;br /&gt;
        document.addEventListener( &#039;DOMContentLoaded&#039;, setupScrollVideos );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
} )();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* =========================&lt;br /&gt;
   YOUTUBE EMBED via JS&lt;br /&gt;
   Template chỉ output data, JS generate full embed&lt;br /&gt;
   ========================= */&lt;br /&gt;
(function () {&lt;br /&gt;
    function buildEmbed(container) {&lt;br /&gt;
        if (container.dataset.initialized === &#039;1&#039;) return;&lt;br /&gt;
        container.dataset.initialized = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
        var videoId = container.dataset.videoId;&lt;br /&gt;
        var width = container.dataset.width || &#039;640&#039;;&lt;br /&gt;
        var align = container.dataset.align || &#039;center&#039;;&lt;br /&gt;
        var caption = container.dataset.caption || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (!videoId) {&lt;br /&gt;
            container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#d33&amp;quot;&amp;gt;Error: missing video ID&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Build container styles&lt;br /&gt;
        container.style.textAlign = align;&lt;br /&gt;
        container.style.margin = &#039;12px 0&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Thumbnail wrapper&lt;br /&gt;
        var thumbWrap = document.createElement(&#039;div&#039;);&lt;br /&gt;
        thumbWrap.className = &#039;soji-yt-thumb-wrap&#039;;&lt;br /&gt;
        thumbWrap.style.cssText = &lt;br /&gt;
            &#039;position: relative;&#039; +&lt;br /&gt;
            &#039;display: inline-block;&#039; +&lt;br /&gt;
            &#039;width: &#039; + width + &#039;px;&#039; +&lt;br /&gt;
            &#039;max-width: 100%;&#039; +&lt;br /&gt;
            &#039;aspect-ratio: 16/9;&#039; +&lt;br /&gt;
            &#039;cursor: pointer;&#039; +&lt;br /&gt;
            &#039;overflow: hidden;&#039; +&lt;br /&gt;
            &#039;border-radius: 8px;&#039; +&lt;br /&gt;
            &#039;background: #000;&#039; +&lt;br /&gt;
            &#039;box-shadow: 0 4px 12px rgba(0,0,0,0.15);&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Thumbnail image&lt;br /&gt;
        var img = document.createElement(&#039;img&#039;);&lt;br /&gt;
        img.src = &#039;https://i.ytimg.com/vi/&#039; + videoId + &#039;/hqdefault.jpg&#039;;&lt;br /&gt;
        img.alt = &#039;Video thumbnail&#039;;&lt;br /&gt;
        img.loading = &#039;lazy&#039;;&lt;br /&gt;
        img.style.cssText = &lt;br /&gt;
            &#039;width: 100%;&#039; +&lt;br /&gt;
            &#039;height: 100%;&#039; +&lt;br /&gt;
            &#039;object-fit: cover;&#039; +&lt;br /&gt;
            &#039;display: block;&#039; +&lt;br /&gt;
            &#039;transition: transform 0.3s, filter 0.2s;&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Try maxresdefault for better quality&lt;br /&gt;
        var hiresTest = new Image();&lt;br /&gt;
        hiresTest.onload = function () {&lt;br /&gt;
            if (hiresTest.naturalWidth &amp;gt; 480) {&lt;br /&gt;
                img.src = &#039;https://i.ytimg.com/vi/&#039; + videoId + &#039;/maxresdefault.jpg&#039;;&lt;br /&gt;
            }&lt;br /&gt;
        };&lt;br /&gt;
        hiresTest.src = &#039;https://i.ytimg.com/vi/&#039; + videoId + &#039;/maxresdefault.jpg&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Play button overlay&lt;br /&gt;
        var overlay = document.createElement(&#039;div&#039;);&lt;br /&gt;
        overlay.className = &#039;soji-yt-play-overlay&#039;;&lt;br /&gt;
        overlay.style.cssText = &lt;br /&gt;
            &#039;position: absolute;&#039; +&lt;br /&gt;
            &#039;top: 50%;&#039; +&lt;br /&gt;
            &#039;left: 50%;&#039; +&lt;br /&gt;
            &#039;transform: translate(-50%, -50%);&#039; +&lt;br /&gt;
            &#039;width: 72px;&#039; +&lt;br /&gt;
            &#039;height: 50px;&#039; +&lt;br /&gt;
            &#039;background: rgba(0, 0, 0, 0.7);&#039; +&lt;br /&gt;
            &#039;border-radius: 14px;&#039; +&lt;br /&gt;
            &#039;display: flex;&#039; +&lt;br /&gt;
            &#039;align-items: center;&#039; +&lt;br /&gt;
            &#039;justify-content: center;&#039; +&lt;br /&gt;
            &#039;transition: background 0.2s, transform 0.2s;&#039; +&lt;br /&gt;
            &#039;pointer-events: none;&#039;;&lt;br /&gt;
&lt;br /&gt;
        overlay.innerHTML = &lt;br /&gt;
            &#039;&amp;lt;svg width=&amp;quot;36&amp;quot; height=&amp;quot;26&amp;quot; viewBox=&amp;quot;0 0 36 26&amp;quot; fill=&amp;quot;white&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;polygon points=&amp;quot;14,7 14,19 25,13&amp;quot;/&amp;gt;&#039; +&lt;br /&gt;
            &#039;&amp;lt;/svg&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
        thumbWrap.appendChild(img);&lt;br /&gt;
        thumbWrap.appendChild(overlay);&lt;br /&gt;
&lt;br /&gt;
        // Hover effect&lt;br /&gt;
        thumbWrap.addEventListener(&#039;mouseenter&#039;, function () {&lt;br /&gt;
            overlay.style.background = &#039;#f00&#039;;&lt;br /&gt;
            overlay.style.transform = &#039;translate(-50%, -50%) scale(1.1)&#039;;&lt;br /&gt;
            img.style.filter = &#039;brightness(0.85)&#039;;&lt;br /&gt;
        });&lt;br /&gt;
        thumbWrap.addEventListener(&#039;mouseleave&#039;, function () {&lt;br /&gt;
            overlay.style.background = &#039;rgba(0, 0, 0, 0.7)&#039;;&lt;br /&gt;
            overlay.style.transform = &#039;translate(-50%, -50%) scale(1)&#039;;&lt;br /&gt;
            img.style.filter = &#039;&#039;;&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // Click → load iframe&lt;br /&gt;
        thumbWrap.addEventListener(&#039;click&#039;, function () {&lt;br /&gt;
            var iframe = document.createElement(&#039;iframe&#039;);&lt;br /&gt;
            iframe.src = &#039;https://www.youtube-nocookie.com/embed/&#039; + videoId + &lt;br /&gt;
                         &#039;?autoplay=1&amp;amp;rel=0&amp;amp;modestbranding=1&amp;amp;playsinline=1&#039;;&lt;br /&gt;
            iframe.style.cssText = &lt;br /&gt;
                &#039;width: 100%;&#039; +&lt;br /&gt;
                &#039;height: 100%;&#039; +&lt;br /&gt;
                &#039;border: 0;&#039; +&lt;br /&gt;
                &#039;position: absolute;&#039; +&lt;br /&gt;
                &#039;inset: 0;&#039;;&lt;br /&gt;
            iframe.setAttribute(&#039;frameborder&#039;, &#039;0&#039;);&lt;br /&gt;
            iframe.setAttribute(&#039;allow&#039;, &#039;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&#039;);&lt;br /&gt;
            iframe.setAttribute(&#039;allowfullscreen&#039;, &#039;true&#039;);&lt;br /&gt;
&lt;br /&gt;
            thumbWrap.innerHTML = &#039;&#039;;&lt;br /&gt;
            thumbWrap.appendChild(iframe);&lt;br /&gt;
            thumbWrap.style.cursor = &#039;default&#039;;&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        container.appendChild(thumbWrap);&lt;br /&gt;
&lt;br /&gt;
        // Caption&lt;br /&gt;
        if (caption) {&lt;br /&gt;
            var captionDiv = document.createElement(&#039;div&#039;);&lt;br /&gt;
            captionDiv.style.cssText = &lt;br /&gt;
                &#039;margin-top: 8px;&#039; +&lt;br /&gt;
                &#039;font-style: italic;&#039; +&lt;br /&gt;
                &#039;color: #666;&#039; +&lt;br /&gt;
                &#039;font-size: 0.9em;&#039;;&lt;br /&gt;
            captionDiv.textContent = caption;&lt;br /&gt;
            container.appendChild(captionDiv);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initYouTubeEmbeds() {&lt;br /&gt;
        document.querySelectorAll(&#039;.soji-yt-embed&#039;).forEach(buildEmbed);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, initYouTubeEmbeds);&lt;br /&gt;
    } else {&lt;br /&gt;
        initYouTubeEmbeds();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ============================================================&lt;br /&gt;
 *  Floating Buttons: move to body level&lt;br /&gt;
 *  Escape mw-body-content stacking context&lt;br /&gt;
 * ============================================================ */&lt;br /&gt;
(function () {&lt;br /&gt;
    function moveFloatingButtonsToBody() {&lt;br /&gt;
        var fb = document.getElementById(&#039;floating-buttons&#039;);&lt;br /&gt;
        if (!fb) return;&lt;br /&gt;
        &lt;br /&gt;
        // Already at body level → skip&lt;br /&gt;
        if (fb.parentElement === document.body) return;&lt;br /&gt;
        &lt;br /&gt;
        document.body.appendChild(fb);&lt;br /&gt;
        &lt;br /&gt;
        // Force styles&lt;br /&gt;
        fb.style.position = &#039;fixed&#039;;&lt;br /&gt;
        fb.style.zIndex = &#039;99999&#039;;&lt;br /&gt;
        fb.style.bottom = &#039;24px&#039;;&lt;br /&gt;
        fb.style.right = &#039;24px&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, moveFloatingButtonsToBody);&lt;br /&gt;
    } else {&lt;br /&gt;
        moveFloatingButtonsToBody();&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    // Re-check sau 1s để chắc chắn (nếu floating-buttons được tạo bằng JS khác sau)&lt;br /&gt;
    setTimeout(moveFloatingButtonsToBody, 1000);&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
/* =========================&lt;br /&gt;
   SCROLL VIDEO - local video with autoplay on scroll&lt;br /&gt;
   ========================= */&lt;br /&gt;
(function () {&lt;br /&gt;
    function buildScrollVideo(container) {&lt;br /&gt;
        if (container.dataset.initialized === &#039;1&#039;) return;&lt;br /&gt;
        container.dataset.initialized = &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
        var src = container.dataset.src;&lt;br /&gt;
        var width = container.dataset.width || &#039;640&#039;;&lt;br /&gt;
        var caption = container.dataset.caption || &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
        if (!src) {&lt;br /&gt;
            container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#d33&amp;quot;&amp;gt;Error: missing video src&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        container.style.textAlign = &#039;center&#039;;&lt;br /&gt;
        container.style.margin = &#039;12px 0&#039;;&lt;br /&gt;
&lt;br /&gt;
        // Create video element&lt;br /&gt;
        var video = document.createElement(&#039;video&#039;);&lt;br /&gt;
        video.className = &#039;scroll-play&#039;;&lt;br /&gt;
        video.width = width;&lt;br /&gt;
        video.muted = true;&lt;br /&gt;
        video.loop = true;&lt;br /&gt;
        video.playsInline = true;&lt;br /&gt;
        video.preload = &#039;metadata&#039;;&lt;br /&gt;
        video.style.cssText = &#039;max-width:100%; border-radius:8px; box-shadow:0 2px 8px rgba(0,0,0,0.1);&#039;;&lt;br /&gt;
&lt;br /&gt;
        var source = document.createElement(&#039;source&#039;);&lt;br /&gt;
        source.src = src;&lt;br /&gt;
        source.type = &#039;video/mp4&#039;;&lt;br /&gt;
        video.appendChild(source);&lt;br /&gt;
        video.appendChild(document.createTextNode(&#039;Your browser does not support HTML5 video.&#039;));&lt;br /&gt;
&lt;br /&gt;
        container.appendChild(video);&lt;br /&gt;
&lt;br /&gt;
        // Caption&lt;br /&gt;
        if (caption) {&lt;br /&gt;
            var cap = document.createElement(&#039;div&#039;);&lt;br /&gt;
            cap.style.cssText = &#039;margin-top:8px; font-style:italic; color:#666; font-size:0.9em;&#039;;&lt;br /&gt;
            cap.textContent = caption;&lt;br /&gt;
            container.appendChild(cap);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // Setup IntersectionObserver for scroll autoplay&lt;br /&gt;
        if (&#039;IntersectionObserver&#039; in window) {&lt;br /&gt;
            var observer = new IntersectionObserver(function (entries) {&lt;br /&gt;
                entries.forEach(function (entry) {&lt;br /&gt;
                    if (entry.isIntersecting) {&lt;br /&gt;
                        video.play().catch(function () {});&lt;br /&gt;
                    } else {&lt;br /&gt;
                        video.pause();&lt;br /&gt;
                    }&lt;br /&gt;
                });&lt;br /&gt;
            }, { threshold: 0.5 });&lt;br /&gt;
            observer.observe(video);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function initScrollVideos() {&lt;br /&gt;
        document.querySelectorAll(&#039;.soji-scroll-video&#039;).forEach(buildScrollVideo);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if (document.readyState === &#039;loading&#039;) {&lt;br /&gt;
        document.addEventListener(&#039;DOMContentLoaded&#039;, initScrollVideos);&lt;br /&gt;
    } else {&lt;br /&gt;
        initScrollVideos();&lt;br /&gt;
    }&lt;br /&gt;
})();&lt;br /&gt;
&lt;br /&gt;
/* ========================================================================&lt;br /&gt;
 * Release Marker Checkbox — Edit form (Source + VisualEditor save dialog)&lt;br /&gt;
 * Tick → append [release] to edit summary → revision shows in history&lt;br /&gt;
 * ======================================================================== */&lt;br /&gt;
( function () {&lt;br /&gt;
	var MARKER = &#039;[release]&#039;;&lt;br /&gt;
	var lastDialogVisible = false;&lt;br /&gt;
&lt;br /&gt;
	function isVisible( el ) {&lt;br /&gt;
		return el &amp;amp;&amp;amp; el.offsetParent !== null;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function isSummaryField( el ) {&lt;br /&gt;
		if ( !el || !isVisible( el ) ) { return false; }&lt;br /&gt;
&lt;br /&gt;
		var id = ( el.id || &#039;&#039; ).toLowerCase();&lt;br /&gt;
		var name = ( el.name || &#039;&#039; ).toLowerCase();&lt;br /&gt;
		if ( id.indexOf( &#039;summary&#039; ) !== -1 || name.indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
&lt;br /&gt;
		var p = el.parentElement;&lt;br /&gt;
		for ( var i = 0; i &amp;lt; 6 &amp;amp;&amp;amp; p; i++ ) {&lt;br /&gt;
			var cls = ( p.className || &#039;&#039; ).toString().toLowerCase();&lt;br /&gt;
			if ( cls.indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
			p = p.parentElement;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var lblId = el.getAttribute( &#039;aria-labelledby&#039; );&lt;br /&gt;
		if ( lblId ) {&lt;br /&gt;
			var lblEl = document.getElementById( lblId );&lt;br /&gt;
			if ( lblEl &amp;amp;&amp;amp; lblEl.textContent.toLowerCase().indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		var fl = el.closest( &#039;.oo-ui-fieldLayout&#039; );&lt;br /&gt;
		if ( fl ) {&lt;br /&gt;
			var label = fl.querySelector( &#039;.oo-ui-labelElement-label, label&#039; );&lt;br /&gt;
			if ( label &amp;amp;&amp;amp; label.textContent.toLowerCase().indexOf( &#039;summary&#039; ) !== -1 ) { return true; }&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		return false;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function findSummaryField() {&lt;br /&gt;
		var all = document.querySelectorAll( &#039;textarea, input[type=&amp;quot;text&amp;quot;]&#039; );&lt;br /&gt;
		for ( var i = 0; i &amp;lt; all.length; i++ ) {&lt;br /&gt;
			if ( isSummaryField( all[ i ] ) ) { return all[ i ]; }&lt;br /&gt;
		}&lt;br /&gt;
		return null;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function getContainer( field ) {&lt;br /&gt;
		return field.closest( &#039;.ve-ui-mwSaveDialog, .oo-ui-dialog-content, .editOptions, form&#039; )&lt;br /&gt;
			|| ( field.parentElement &amp;amp;&amp;amp; field.parentElement.parentElement )&lt;br /&gt;
			|| document;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function cleanupStaleWrappers() {&lt;br /&gt;
		document.querySelectorAll( &#039;.release-checkbox-wrapper&#039; ).forEach( function ( w ) {&lt;br /&gt;
			if ( !isVisible( w ) ) { w.remove(); }&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function resetAllCheckboxes() {&lt;br /&gt;
		document.querySelectorAll( &#039;.mark-as-release-cb&#039; ).forEach( function ( cb ) {&lt;br /&gt;
			cb.checked = false;&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function injectCheckbox( field ) {&lt;br /&gt;
		if ( !field ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var anchor =&lt;br /&gt;
			field.closest( &#039;.ve-ui-mwSaveDialog-summary&#039; ) ||&lt;br /&gt;
			field.closest( &#039;.oo-ui-fieldLayout&#039; ) ||&lt;br /&gt;
			field.closest( &#039;.oo-ui-textInputWidget&#039; ) ||&lt;br /&gt;
			field.parentElement;&lt;br /&gt;
		if ( !anchor || !anchor.parentNode ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var container = getContainer( field );&lt;br /&gt;
		var existing = container.querySelector( &#039;.release-checkbox-wrapper&#039; );&lt;br /&gt;
		if ( existing &amp;amp;&amp;amp; isVisible( existing ) ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var wrapper = document.createElement( &#039;div&#039; );&lt;br /&gt;
		wrapper.className = &#039;release-checkbox-wrapper&#039;;&lt;br /&gt;
		wrapper.style.cssText = &#039;margin:8px 0;padding:8px 12px;background:#f0f8ff;border-left:3px solid #1f4e79;border-radius:3px;font-size:13px;display:block;box-sizing:border-box;&#039;;&lt;br /&gt;
		wrapper.innerHTML =&lt;br /&gt;
			&#039;&amp;lt;label style=&amp;quot;cursor:pointer;font-weight:500;display:block;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
			&#039;&amp;lt;input type=&amp;quot;checkbox&amp;quot; class=&amp;quot;mark-as-release-cb&amp;quot; style=&amp;quot;margin-right:6px;vertical-align:middle;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
			&#039;Mark as published revision&#039; +&lt;br /&gt;
			&#039;&amp;lt;/label&amp;gt;&#039; +&lt;br /&gt;
			&#039;&amp;lt;div style=&amp;quot;font-size:11px;color:#666;margin-top:3px;margin-left:22px;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
			&#039;Tick to add this edit to the Revision History table.&#039; +&lt;br /&gt;
			&#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
		anchor.parentNode.insertBefore( wrapper, anchor );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function applyMarkerIfChecked() {&lt;br /&gt;
		var field = findSummaryField();&lt;br /&gt;
		if ( !field ) { return; }&lt;br /&gt;
&lt;br /&gt;
		var container = getContainer( field );&lt;br /&gt;
		var cb = container.querySelector( &#039;.mark-as-release-cb&#039; );&lt;br /&gt;
		if ( !cb || !isVisible( cb ) || !cb.checked ) { return; }&lt;br /&gt;
&lt;br /&gt;
		if ( field.value.indexOf( MARKER ) === -1 ) {&lt;br /&gt;
			field.value = ( field.value.trim() ? field.value.trim() + &#039; &#039; : &#039;&#039; ) + MARKER;&lt;br /&gt;
			field.dispatchEvent( new Event( &#039;input&#039;, { bubbles: true } ) );&lt;br /&gt;
			field.dispatchEvent( new Event( &#039;change&#039;, { bubbles: true } ) );&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		// Reset checkbox after applying — prevents stale state on next save&lt;br /&gt;
		cb.checked = false;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	document.addEventListener( &#039;click&#039;, function ( e ) {&lt;br /&gt;
		var btn = e.target.closest( &#039;button, a[role=&amp;quot;button&amp;quot;], .oo-ui-buttonElement-button&#039; );&lt;br /&gt;
		if ( !btn ) { return; }&lt;br /&gt;
		var text = ( btn.textContent || &#039;&#039; ).trim().toLowerCase();&lt;br /&gt;
		if (&lt;br /&gt;
			text === &#039;save changes&#039; || text === &#039;save page&#039; ||&lt;br /&gt;
			text === &#039;publish changes&#039; || text === &#039;publish page&#039; ||&lt;br /&gt;
			text === &#039;lưu thay đổi&#039; || text === &#039;lưu trang&#039; ||&lt;br /&gt;
			btn.id === &#039;wpSave&#039;&lt;br /&gt;
		) {&lt;br /&gt;
			applyMarkerIfChecked();&lt;br /&gt;
		}&lt;br /&gt;
	}, true );&lt;br /&gt;
&lt;br /&gt;
	document.addEventListener( &#039;submit&#039;, function ( e ) {&lt;br /&gt;
		var form = e.target;&lt;br /&gt;
		if ( form &amp;amp;&amp;amp; ( form.id === &#039;editform&#039; || form.querySelector( &#039;#wpSummary&#039; ) ) ) {&lt;br /&gt;
			applyMarkerIfChecked();&lt;br /&gt;
		}&lt;br /&gt;
	}, true );&lt;br /&gt;
&lt;br /&gt;
	function scan() {&lt;br /&gt;
		cleanupStaleWrappers();&lt;br /&gt;
		var field = findSummaryField();&lt;br /&gt;
		var dialogVisible = !!field;&lt;br /&gt;
&lt;br /&gt;
		// Reset checkboxes when dialog transitions from closed -&amp;gt; open&lt;br /&gt;
		if ( dialogVisible &amp;amp;&amp;amp; !lastDialogVisible ) {&lt;br /&gt;
			resetAllCheckboxes();&lt;br /&gt;
		}&lt;br /&gt;
		lastDialogVisible = dialogVisible;&lt;br /&gt;
&lt;br /&gt;
		if ( field ) { injectCheckbox( field ); }&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	if ( document.readyState === &#039;loading&#039; ) {&lt;br /&gt;
		document.addEventListener( &#039;DOMContentLoaded&#039;, scan );&lt;br /&gt;
	} else {&lt;br /&gt;
		scan();&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	var observer = new MutationObserver( scan );&lt;br /&gt;
	observer.observe( document.body, {&lt;br /&gt;
		childList: true,&lt;br /&gt;
		subtree: true,&lt;br /&gt;
		attributes: true,&lt;br /&gt;
		attributeFilter: [ &#039;style&#039;, &#039;class&#039; ]&lt;br /&gt;
	} );&lt;br /&gt;
&lt;br /&gt;
	setInterval( scan, 500 );&lt;br /&gt;
}() );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* ========================================================================&lt;br /&gt;
 * Revision History Renderer — show only revisions tagged with [release]&lt;br /&gt;
 * Auto-version v1.0, v1.1, v1.2 ... in chronological order&lt;br /&gt;
 * ======================================================================== */&lt;br /&gt;
( function () {&lt;br /&gt;
	function formatDate( ts ) {&lt;br /&gt;
		var d = new Date( ts );&lt;br /&gt;
		var dd = String( d.getDate() ).padStart( 2, &#039;0&#039; );&lt;br /&gt;
		var mm = String( d.getMonth() + 1 ).padStart( 2, &#039;0&#039; );&lt;br /&gt;
		var yyyy = d.getFullYear();&lt;br /&gt;
		return dd + &#039;/&#039; + mm + &#039;/&#039; + yyyy;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function hasReleaseMarker( comment ) {&lt;br /&gt;
		if ( !comment ) { return false; }&lt;br /&gt;
		return /(^|\s)\[release\](\s|$)/.test( comment );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function cleanComment( comment ) {&lt;br /&gt;
		if ( !comment ) { return &#039;Initial Release&#039;; }&lt;br /&gt;
		var cleaned = comment&lt;br /&gt;
			.replace( /\[release\]/g, &#039;&#039; )&lt;br /&gt;
			.replace( /\/\*[^*]*\*\//g, &#039;&#039; )&lt;br /&gt;
			.trim();&lt;br /&gt;
		return cleaned || &#039;Updated&#039;;&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function renderRevisionHistory( container ) {&lt;br /&gt;
		var api = new mw.Api();&lt;br /&gt;
		api.get( {&lt;br /&gt;
			action: &#039;query&#039;,&lt;br /&gt;
			prop: &#039;revisions&#039;,&lt;br /&gt;
			titles: mw.config.get( &#039;wgPageName&#039; ),&lt;br /&gt;
			rvprop: &#039;timestamp|user|comment|ids&#039;,&lt;br /&gt;
			rvlimit: 500&lt;br /&gt;
		} ).done( function ( data ) {&lt;br /&gt;
			var pages = data.query &amp;amp;&amp;amp; data.query.pages;&lt;br /&gt;
			if ( !pages ) { return; }&lt;br /&gt;
			var pageId = Object.keys( pages )[ 0 ];&lt;br /&gt;
			var revisions = pages[ pageId ].revisions || [];&lt;br /&gt;
&lt;br /&gt;
			var releases = revisions.filter( function ( r ) {&lt;br /&gt;
				return hasReleaseMarker( r.comment );&lt;br /&gt;
			} );&lt;br /&gt;
&lt;br /&gt;
			if ( !releases.length ) {&lt;br /&gt;
				container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#888;&amp;quot;&amp;gt;No published revisions yet. Tick &amp;quot;Mark as published revision&amp;quot; when saving an edit to add an entry here.&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
				return;&lt;br /&gt;
			}&lt;br /&gt;
&lt;br /&gt;
			releases.sort( function ( a, b ) {&lt;br /&gt;
				return new Date( a.timestamp ) - new Date( b.timestamp );&lt;br /&gt;
			} );&lt;br /&gt;
			releases.forEach( function ( r, i ) {&lt;br /&gt;
				r.version = &#039;v1.&#039; + i;&lt;br /&gt;
			} );&lt;br /&gt;
			releases.reverse();&lt;br /&gt;
&lt;br /&gt;
			var html = &#039;&amp;lt;table class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:100%;&amp;quot;&amp;gt;&#039; +&lt;br /&gt;
				&#039;&amp;lt;thead&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Date&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Version&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Author&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Description&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;lt;/thead&amp;gt;&#039; +&lt;br /&gt;
				&#039;&amp;lt;tbody&amp;gt;&#039;;&lt;br /&gt;
			releases.forEach( function ( r ) {&lt;br /&gt;
				html +=&lt;br /&gt;
					&#039;&amp;lt;tr&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&#039; + formatDate( r.timestamp ) + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&amp;lt;strong&amp;gt;&#039; + r.version + &#039;&amp;lt;/strong&amp;gt;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&#039; + mw.html.escape( r.user ) + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;td&amp;gt;&#039; + mw.html.escape( cleanComment( r.comment ) ) + &#039;&amp;lt;/td&amp;gt;&#039; +&lt;br /&gt;
					&#039;&amp;lt;/tr&amp;gt;&#039;;&lt;br /&gt;
			} );&lt;br /&gt;
			html += &#039;&amp;lt;/tbody&amp;gt;&amp;lt;/table&amp;gt;&#039;;&lt;br /&gt;
			container.innerHTML = html;&lt;br /&gt;
		} ).fail( function () {&lt;br /&gt;
			container.innerHTML = &#039;&amp;lt;em style=&amp;quot;color:#c00;&amp;quot;&amp;gt;Failed to load revision history.&amp;lt;/em&amp;gt;&#039;;&lt;br /&gt;
		} );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	mw.hook( &#039;wikipage.content&#039; ).add( function ( $content ) {&lt;br /&gt;
		$content.find( &#039;.revision-history-auto&#039; ).each( function () {&lt;br /&gt;
			renderRevisionHistory( this );&lt;br /&gt;
		} );&lt;br /&gt;
	} );&lt;br /&gt;
}() );&lt;br /&gt;
&lt;br /&gt;
/* ========================================================================&lt;br /&gt;
 * PDF TOC Generator with JS-measured page numbers&lt;br /&gt;
 * No forced page breaks - measures actual heading positions&lt;br /&gt;
 * ======================================================================== */&lt;br /&gt;
/* ===== PDF mode (pdf-service entry point) ===== */&lt;br /&gt;
( function () {&lt;br /&gt;
	&#039;use strict&#039;;&lt;br /&gt;
&lt;br /&gt;
	var params = new URLSearchParams( location.search );&lt;br /&gt;
	var pdfMode = params.get( &#039;pdfmode&#039; ) === &#039;1&#039;;&lt;br /&gt;
&lt;br /&gt;
	if ( pdfMode ) {&lt;br /&gt;
		document.documentElement.classList.add( &#039;pdfmode&#039; );&lt;br /&gt;
		document.body &amp;amp;&amp;amp; document.body.classList.add( &#039;pdfmode&#039; );&lt;br /&gt;
		document.addEventListener( &#039;DOMContentLoaded&#039;, function () {&lt;br /&gt;
			document.body.classList.add( &#039;pdfmode&#039; );&lt;br /&gt;
		} );&lt;br /&gt;
&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	function addDownloadLink() {&lt;br /&gt;
		var title = ( mw.config &amp;amp;&amp;amp; mw.config.get( &#039;wgPageName&#039; ) ) || &#039;&#039;;&lt;br /&gt;
		var ns = mw.config &amp;amp;&amp;amp; mw.config.get( &#039;wgNamespaceNumber&#039; );&lt;br /&gt;
		if ( !title || ns !== 0 ) { return; }&lt;br /&gt;
&lt;br /&gt;
		// Show only on Datasheet / User Guide / Protocol pages, or Software &amp;amp; Applications category&lt;br /&gt;
		var titleMatches = /(Datasheet|User[_ ]Guide|Protocol)/i.test( title );&lt;br /&gt;
		var categories = ( mw.config &amp;amp;&amp;amp; mw.config.get( &#039;wgCategories&#039; ) ) || [];&lt;br /&gt;
		var inSoftwareCat = categories.some( function ( c ) {&lt;br /&gt;
			return /Software\s*&amp;amp;?\s*Applications/i.test( c );&lt;br /&gt;
		} );&lt;br /&gt;
		if ( !titleMatches &amp;amp;&amp;amp; !inSoftwareCat ) { return; }&lt;br /&gt;
&lt;br /&gt;
		if ( document.querySelector( &#039;.pdf-download-btn&#039; ) ) { return; }&lt;br /&gt;
		var anchor = document.querySelector( &#039;#firstHeading&#039; );&lt;br /&gt;
		if ( !anchor ) { return; }&lt;br /&gt;
		var btn = document.createElement( &#039;a&#039; );&lt;br /&gt;
		btn.className = &#039;pdf-download-btn&#039;;&lt;br /&gt;
		btn.href = &#039;/api/pdf?page=&#039; + encodeURIComponent( title );&lt;br /&gt;
		btn.target = &#039;_blank&#039;;&lt;br /&gt;
		btn.rel = &#039;noopener&#039;;&lt;br /&gt;
		btn.innerHTML = &#039;&amp;lt;span class=&amp;quot;pdf-download-icon&amp;quot;&amp;gt;\u2B07&amp;lt;/span&amp;gt; Download PDF&#039;;&lt;br /&gt;
		anchor.parentNode.insertBefore( btn, anchor.nextSibling );&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if ( !pdfMode &amp;amp;&amp;amp; window.mw &amp;amp;&amp;amp; mw.hook ) {&lt;br /&gt;
		mw.hook( &#039;wikipage.content&#039; ).add( addDownloadLink );&lt;br /&gt;
	}&lt;br /&gt;
}() );&lt;br /&gt;
&lt;br /&gt;
$(function () {&lt;br /&gt;
    // Thứ tự mong muốn (viết thường). Thêm/bớt loại tài liệu tại đây.&lt;br /&gt;
    var ORDER = [&#039;datasheet&#039;, &#039;user guide&#039;, &#039;protocol&#039;, &#039;video&#039;];&lt;br /&gt;
&lt;br /&gt;
    function rank(text) {&lt;br /&gt;
        text = (text || &#039;&#039;).toLowerCase();&lt;br /&gt;
        for (var i = 0; i &amp;lt; ORDER.length; i++) {&lt;br /&gt;
            if (text.indexOf(ORDER[i]) !== -1) return i;&lt;br /&gt;
        }&lt;br /&gt;
        return ORDER.length; // loại chưa khai báo -&amp;gt; xuống cuối&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function sortSidebar() {&lt;br /&gt;
        // Giới hạn trong vùng sidebar (chỉnh selector nếu cần)&lt;br /&gt;
        var scope = document.querySelector(&#039;#mw-panel, .vector-main-menu, #p-categorytree-portlet&#039;) || document;&lt;br /&gt;
        var links = scope.querySelectorAll(&#039;a&#039;);&lt;br /&gt;
        var groups = new Map();&lt;br /&gt;
&lt;br /&gt;
        links.forEach(function (a) {&lt;br /&gt;
            if (rank(a.textContent) &amp;lt; ORDER.length) {&lt;br /&gt;
                // phần tử item để di chuyển = li / item bao quanh link&lt;br /&gt;
                var item = a.closest(&#039;li, .CategoryTreeItem, .CategoryTreeSection&#039;) || a.parentNode;&lt;br /&gt;
                var parent = item.parentNode;&lt;br /&gt;
                if (!parent) return;&lt;br /&gt;
                if (!groups.has(parent)) groups.set(parent, []);&lt;br /&gt;
                groups.get(parent).push(item);&lt;br /&gt;
            }&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        groups.forEach(function (items, parent) {&lt;br /&gt;
            var sorted = items.slice().sort(function (x, y) {&lt;br /&gt;
                return rank(x.textContent) - rank(y.textContent);&lt;br /&gt;
            });&lt;br /&gt;
            // chỉ sắp lại nếu thực sự khác (tránh lặp vô hạn với observer)&lt;br /&gt;
            var changed = sorted.some(function (el, i) { return el !== items[i]; });&lt;br /&gt;
            if (changed) sorted.forEach(function (el) { parent.appendChild(el); });&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    sortSidebar();&lt;br /&gt;
&lt;br /&gt;
    // CategoryTree load con theo kiểu lazy (bấm mới hiện) -&amp;gt; theo dõi và sắp lại&lt;br /&gt;
    var root = document.querySelector(&#039;#mw-panel&#039;) || document.body;&lt;br /&gt;
    if (root) {&lt;br /&gt;
        new MutationObserver(function () { sortSidebar(); })&lt;br /&gt;
            .observe(root, { childList: true, subtree: true });&lt;br /&gt;
    }&lt;br /&gt;
});&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Datasheet&amp;diff=2052</id>
		<title>LIGO PRO 2 Datasheet</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Datasheet&amp;diff=2052"/>
		<updated>2026-06-24T10:45:32Z</updated>

		<summary type="html">&lt;p&gt;Admin: Initial Release [release]&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Product Overview ==&lt;br /&gt;
&lt;br /&gt;
The Ligo PRO 2 Fuel Level Sensor is produced and developed by SOJI Electronics JSC. The device is designed to measure the level of liquid fuel and other non-conductive liquids in vehicle tanks and stationary fuel storage, applicable across different fields. The measured values are transmitted to an external device as output signals such as Analog, Frequency, RS232, or RS485.&lt;br /&gt;
&lt;br /&gt;
Ligo PRO 2 was created to satisfy the increasing demand of the transport telematics market for improved reliability of design, data accuracy, and ease of installation. The sensor is integrated with Bluetooth Low Energy (BLE) making installation more convenient, easier, and faster.&lt;br /&gt;
&lt;br /&gt;
In addition, the heavy-duty metal structure helps increase protection against electrostatic, electromagnetic and conducted interference. The Ligo PRO 2 is also designed with IP69K waterproof standards and superior shock and vibration resistance.&lt;br /&gt;
&lt;br /&gt;
== Key Features ==&lt;br /&gt;
&lt;br /&gt;
*High accuracy up to 99.5%&lt;br /&gt;
*Wide range input voltage: 6-40V&lt;br /&gt;
*Galvanic isolation up to 2500V&lt;br /&gt;
*Configurable length up to 6000mm&lt;br /&gt;
*Operating temperature: -40°C to +85°C&lt;br /&gt;
*Built-in anti-mud oil filter and anti-vibration springs&lt;br /&gt;
*IP69K waterproof standard&lt;br /&gt;
*Integrated BLE technology for smartphone configuration&lt;br /&gt;
&lt;br /&gt;
== Applications ==&lt;br /&gt;
&lt;br /&gt;
*Trucks, excavators, heavy construction machines&lt;br /&gt;
*Marine vessels and electric generators&lt;br /&gt;
*Off-highway machinery including high-temperature and vibration environments&lt;br /&gt;
*Industrial oil storage tanks or stationary storage tanks&lt;br /&gt;
&lt;br /&gt;
== Product Variants / Ordering Information ==&lt;br /&gt;
&lt;br /&gt;
== Technical Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500 ... up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0-9V) || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || colspan=&amp;quot;3&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V) || colspan=&amp;quot;3&amp;quot; | 6-40V&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Supply protection (Over-voltage 100 VDC for 2 min. Reverse polarity) || colspan=&amp;quot;3&amp;quot; | Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40 ... +85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level, % || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output range (min level) || Analog (0...8V) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output range (max level) || Analog (1...9V) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 0 ... 255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, s || Continuous || 1 ... 255 || 1 ... 255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement, °C || colspan=&amp;quot;3&amp;quot; | ±1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.1, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity, +8 dBm TX Power&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || colspan=&amp;quot;3&amp;quot; | 10&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Dimensions ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig1-dimensions.jpg|frameless|915px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Ligo PRO 2 overall dimensions (cm)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Wiring Connection ===&lt;br /&gt;
&lt;br /&gt;
==== AF (Analog &amp;amp; Frequency) ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig2-wiring-AF.png|frameless|892px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Wiring diagram – Analog and Frequency output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== RS232 ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig3-wiring-RS232.png|frameless|903px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Wiring diagram – RS232 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== RS485 ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig4-wiring-RS485.png|frameless|867px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Wiring diagram – RS485 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Connector ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-PRO-2-fig5-connector.png|frameless|703px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. Connector pin details&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== AF / RS232 Pinout ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Description !! Wire Color&lt;br /&gt;
|-&lt;br /&gt;
| 1 || GND || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 2 || NC (Not connected) || NC&lt;br /&gt;
|-&lt;br /&gt;
| 3 || VCC (6-40V) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || TXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 5 || Analog Output || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#fff;border:1px solid #999;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;White&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 6 || RXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#7b3f00;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Brown&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== RS485 Pinout ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Pin !! Description !! Wire Color&lt;br /&gt;
|-&lt;br /&gt;
| 1 || GND || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 2 || RS485 (B) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#2e8b2e;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Green&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 3 || VCC (6-40V) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 4 || TXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 5 || RS485 (A) || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 6 || RXD || &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#7b3f00;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Brown&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Compliance and Certifications ==&lt;br /&gt;
Refer to [[LIGO PRO 2 Certification &amp;amp; Approvals]] for current certificate status.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2 ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_2_Datasheet&amp;diff=2051</id>
		<title>Category:LIGO PRO 2 Datasheet</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_2_Datasheet&amp;diff=2051"/>
		<updated>2026-06-24T10:45:23Z</updated>

		<summary type="html">&lt;p&gt;Admin: Auto-created category for LIGO PRO 2 Datasheet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- auto-created category --&amp;gt;&lt;br /&gt;
{{DEFAULTSORT:05 LIGO PRO 2 Datasheet}}&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Datasheet&amp;diff=2050</id>
		<title>LIGO PRO 2 Datasheet</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_Datasheet&amp;diff=2050"/>
		<updated>2026-06-24T10:45:23Z</updated>

		<summary type="html">&lt;p&gt;Admin: Create page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Product Overview ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Brief 1–2 paragraph introduction to the product per IEC/IEEE 82079-1.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* Product name, category, primary function&lt;br /&gt;
* Key technology and target environment&lt;br /&gt;
* Value proposition for B2B customers&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example structure:&#039;&#039;&#039; &amp;quot;The [Product Name] is a [type] device designed for [primary application]. It features [key technology] and is built for [target environment].&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Key Features ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bullet list of top 5–7 differentiating features.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* Each bullet = 1 key feature with measurable value&lt;br /&gt;
* Focus on: accuracy, connectivity, durability, compatibility&lt;br /&gt;
* Examples of features to cover:&lt;br /&gt;
** Measurement specifications (accuracy, range, response time)&lt;br /&gt;
** Connectivity (Bluetooth/WiFi/Cellular protocol + version)&lt;br /&gt;
** Power (battery life, voltage range)&lt;br /&gt;
** Environmental rating (IP, operating temperature)&lt;br /&gt;
** Compatibility (telematics platforms, software)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Applications ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Typical use cases and industries the product serves.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039; bullet list of applications (4–6 items)&lt;br /&gt;
* Each bullet = 1 specific industry/use case&lt;br /&gt;
* Common categories:&lt;br /&gt;
** Transportation/Fleet (trucks, buses, trailers)&lt;br /&gt;
** Telecom (backup generators, base stations)&lt;br /&gt;
** Industrial (storage tanks, process monitoring)&lt;br /&gt;
** Marine (bunker fuel, hydraulic systems)&lt;br /&gt;
** Mining (heavy equipment, refueling stations)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Product Variants / Ordering Information ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Table of all product SKUs available for ordering.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039; table with columns:&lt;br /&gt;
* &#039;&#039;&#039;Model&#039;&#039;&#039; — short product code (e.g., LIGO-AIR-STD)&lt;br /&gt;
* &#039;&#039;&#039;Part Number&#039;&#039;&#039; — internal SKU (e.g., SKU-001)&lt;br /&gt;
* &#039;&#039;&#039;Description&#039;&#039;&#039; — variant differences (cable length, certification, etc.)&lt;br /&gt;
* &#039;&#039;&#039;Region&#039;&#039;&#039; — geographic availability (Global, EU/APAC, US only, etc.)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Common variants:&#039;&#039;&#039; Standard / Extended / Industrial-grade / ATEX-certified&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Technical Specifications ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Detailed technical parameters of the product.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039; specification table with columns Parameter / Value / Unit / Descriptions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Parameters to cover:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Electrical&#039;&#039;&#039;: Operating voltage, Operating current (avg/peak/sleep), Power consumption&lt;br /&gt;
* &#039;&#039;&#039;Performance&#039;&#039;&#039;: Measurement range, Accuracy, Resolution, Response time, Sampling rate&lt;br /&gt;
* &#039;&#039;&#039;Wireless&#039;&#039;&#039; (if applicable): Protocol, Frequency band, Output power, Receiver sensitivity, Range&lt;br /&gt;
* &#039;&#039;&#039;Environmental&#039;&#039;&#039;: Operating temperature, Storage temperature, Humidity, Ingress protection (IP)&lt;br /&gt;
* &#039;&#039;&#039;Mechanical&#039;&#039;&#039;: Dimensions (L × W × H), Weight, Mounting interface&lt;br /&gt;
* &#039;&#039;&#039;Service life&#039;&#039;&#039;: Expected years at typical operation&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Optional sub-sections:&#039;&#039;&#039; If product has many specs, split into &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;=== Performance ===&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;=== Electrical ===&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;=== Wireless ===&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;=== Environmental ===&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;=== Memory ===&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;=== Mechanical Dimensions ===&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;=== Interface / Connectivity ===&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;, each with its own table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Compliance and Certifications ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Regulatory certifications and standards compliance.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in (sub-sections):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Wireless / Radio&#039;&#039;&#039; — bullet list of certifications by region:&lt;br /&gt;
** CE RED (EU) — certificate number&lt;br /&gt;
** FCC Part 15 (US) — FCC ID&lt;br /&gt;
** IC (Canada) — IC number&lt;br /&gt;
** Bluetooth SIG QDID (if BLE)&lt;br /&gt;
** RoHS 3&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Environmental&#039;&#039;&#039; — standards compliance:&lt;br /&gt;
** RoHS 3 (Directive 2015/863/EU)&lt;br /&gt;
** REACH (EC 1907/2006)&lt;br /&gt;
** WEEE (Directive 2012/19/EU)&lt;br /&gt;
** IP rating per IEC 60529&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Mechanical / Safety&#039;&#039;&#039; — test standards:&lt;br /&gt;
** IEC 60068-2-6 — Vibration&lt;br /&gt;
** IEC 60068-2-27 — Shock&lt;br /&gt;
** IEC 60068-2-30 — Damp heat&lt;br /&gt;
** IEC 61010-1 — Electrical safety&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Industry-Specific&#039;&#039;&#039; (remove if N/A):&lt;br /&gt;
** ATEX / IECEx — for hazardous environments&lt;br /&gt;
** MID — Measuring Instruments Directive&lt;br /&gt;
** Automotive E-Mark — for vehicle applications&lt;br /&gt;
** Marine certification — DNV / ABS / Lloyd&#039;s Register&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Warranty&#039;&#039;&#039;:&lt;br /&gt;
** Standard warranty period (years from shipment)&lt;br /&gt;
** Extended warranty options&lt;br /&gt;
** NOT covered items&lt;br /&gt;
** Void conditions&lt;br /&gt;
** Cross-reference to [[Warranty Terms and Conditions]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2 ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_User_Guide&amp;diff=2049</id>
		<title>LIGO PRO 2 User Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_User_Guide&amp;diff=2049"/>
		<updated>2026-06-24T10:44:27Z</updated>

		<summary type="html">&lt;p&gt;Admin: Initial Release [release]&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Safety Information ==&lt;br /&gt;
{{Notice&lt;br /&gt;
| title = Installer Qualifications and Safety&lt;br /&gt;
| body = *LIGO SP Fuel Level Sensor measures the accurate fuel level in fuel tanks, therefore the device installation process also needs to be extremely accurate.&lt;br /&gt;
*Installation technicians should be well-trained by specialists or experts in this field before installing the device.&lt;br /&gt;
*Implement protection against electric shocks and electrical fires, labour safety and hygiene.&lt;br /&gt;
*List of installing tools is provided in section APPENDIX A below.&lt;br /&gt;
*List of accessories is provided in section APPENDIX B below.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== Product Overview ==&lt;br /&gt;
LIGO Fuel level sensor is produced and developed by SOJI Electronics Join Stock Company. The device is designed to measure the level of liquid fuels and other non-conductive liquids in vehicle&#039;s tanks and stationary fuel storages, applicable in different fields. The measured values will be t transformed into output signal in the form of: Analog, Frequency, RS232, RS485… and transmitted to a connected external device&lt;br /&gt;
&lt;br /&gt;
Under particular conditions, the device can reach a high accuracy up to 99.5%. At present, on the market there are several lines of sensor using different technologies to measure fuel level such as: magnetically operated switches (reed switches), ultrasound wave (ultrasonic sensor), capacitive sensing (capacitive sensor). Among these ones, capacitive sensing technology is considered to have highest accuracy and greatest lifespan.&lt;br /&gt;
&lt;br /&gt;
== Package Contents ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! STT !! Description !! Qty&lt;br /&gt;
|-&lt;br /&gt;
| 1 || style=&amp;quot;text-align:left;&amp;quot; | Ligo PRO 2 Fuel Level Sensor. Standard lengths: 700, 1000, and 1500mm (for other customized lengths, please contact the manufacturer) || 01&lt;br /&gt;
|-&lt;br /&gt;
| 2 || style=&amp;quot;text-align:left;&amp;quot; | Oil filter || 01&lt;br /&gt;
|-&lt;br /&gt;
| 3 || style=&amp;quot;text-align:left;&amp;quot; | Gasoline resistant rubber gasket || 01&lt;br /&gt;
|-&lt;br /&gt;
| 4 || style=&amp;quot;text-align:left;&amp;quot; | Anti-vibration spring when moving || 01&lt;br /&gt;
|-&lt;br /&gt;
| 5 || style=&amp;quot;text-align:left;&amp;quot; | 1A Fuse protection || 01&lt;br /&gt;
|-&lt;br /&gt;
| 6 || style=&amp;quot;text-align:left;&amp;quot; | Self-drilling screw M4.8x32mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 7 || style=&amp;quot;text-align:left;&amp;quot; | Rivet and screw M5x20mm || 04&lt;br /&gt;
|-&lt;br /&gt;
| 8 || style=&amp;quot;text-align:left;&amp;quot; | Sealing wire || 02&lt;br /&gt;
|-&lt;br /&gt;
| 9 || style=&amp;quot;text-align:left;&amp;quot; | 7m signal wire || 01&lt;br /&gt;
|-&lt;br /&gt;
| 10 || style=&amp;quot;text-align:left;&amp;quot; | Quick installation manual || 01&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Parameter !! AF !! RS232 !! RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Standard length (L), mm || colspan=&amp;quot;3&amp;quot; | 700, 1000, 1500 ... up to 6000 mm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measuring error, % || colspan=&amp;quot;3&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output signal || Analog (0-9V) || RS232 || RS485&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Baud rate, bit/sec || colspan=&amp;quot;3&amp;quot; | 2400, 4800, 9600, 19200, 38400, 57600, 115200&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Power supply (DC input voltage, V) || colspan=&amp;quot;3&amp;quot; | 6-40V&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Supply protection (Over-voltage 100 VDC for 2 min. Reverse polarity) || colspan=&amp;quot;3&amp;quot; | Yes&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum power consumption, mA || colspan=&amp;quot;3&amp;quot; | 20&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (IP) || colspan=&amp;quot;3&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature, °C || colspan=&amp;quot;3&amp;quot; | -40 ... +85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Maximum allowed humidity level, % || colspan=&amp;quot;3&amp;quot; | 100&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution, bit || colspan=&amp;quot;3&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output range (min level) || Analog (0...8V) || 0 || 0&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Output range (max level) || Analog (1...9V) || 4095 || 4095&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average sampling period, s || colspan=&amp;quot;3&amp;quot; | 0 ... 255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Message interval, s || Continuous || 1 ... 255 || 1 ... 255&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Absolute error in temperature measurement, °C || colspan=&amp;quot;3&amp;quot; | ±1&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;3&amp;quot; | Bluetooth 5.1, IEEE 802.15.4-2006, 2.4 GHz, -95 dBm sensitivity, +8 dBm TX Power&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Average service life, years (expected) || colspan=&amp;quot;3&amp;quot; | 10&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&lt;br /&gt;
=== Selecting the location to install sensor ===&lt;br /&gt;
&lt;br /&gt;
* Installation location should be as close as possible to the geometric center and placed at the deepest level of the tank.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig1-location.png|frameless|894px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 1. Selecting the location to install sensor&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When being installed, the sensor should not be in contact with reinforcement ribs inside the tank&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig2-two-sensors.png|frameless|730px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 2. Installation of two sensors in one tank allows for significant reduction&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Drilling the hole on fuel tank ===&lt;br /&gt;
&lt;br /&gt;
* Drill out the central bore by bimetal core drill ø38 mm&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig3-drilling.png|frameless|580px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 3. Drilling the selected hole&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Drill out four mounting holes according to the diagram:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig4-mounting-holes.png|frameless|541px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 4. Drill out four mounting holes&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The mounting hole diameter depends on the tank material:&lt;br /&gt;
&lt;br /&gt;
* ø 4 mm – for metal tank with wall thickness over 3 mm (cut M5 self screw M4.8)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig5-hole-thick.png|frameless|402px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 5. Hole thick&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ø 7 mm – for plastic and metal tank with wall thickness up to 3 mm (for rivets)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig6-rivet-nut.png|frameless|372px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 6. Rivet nut M5&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig7-install-rivet.png|frameless|535px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 7. Installing revit nut if tank&#039;s wall is thinner than 3mm&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Measuaring the tank depth and cutting sensor&#039;measuring probe ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig8-measure-depth.png|frameless|615px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 8. Measuring the tank depth&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Cut the sensor&#039;s measuring probe so that its length is 25 mm less than the depth of the tank. Ensure that shearing line is perpendicular to the sensor longitudinal axis&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig9-length.png|frameless|723px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 9. Origin length (L) and length after cutting (Lm)&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes: Minimum allowable length of the measuring probe is 150 mm.&lt;br /&gt;
&lt;br /&gt;
=== Installing screen filter ===&lt;br /&gt;
Screen filter is mounted on the measuring probe of sensor to protect measuring probe electrodes from mud and water. Using the filter extends significantly faultless lifetime of the sensor.&lt;br /&gt;
&lt;br /&gt;
Screen filter mounting order: firstly put on fixator, then put on bottom stop and fix it with two side screws. Put the screen filter over bottom stop and fasten it with a fixator locks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig15-screen-filter.png|frameless|747px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 10. Installing screen filter procedue&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Installing and connecting ===&lt;br /&gt;
&lt;br /&gt;
* Applying oil-and-petrol resistant non-conductive sealant to ruber gasket&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig16-sealant.png|frameless|595px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 11. Applying the sealant to gasket&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Put the sensor into the tank and fix: when fixing with rivets, use a rivet driver when bolting, put on a seal (per bolt), a spacer and a spring washer when fixing to plastic tanks with wall thickness over 3 mm, use vendor furnished self-tapping screws and a seal (per self-tapping screw)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig17-install.png|frameless|663px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 12. Put sensor into the tank&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Connect the sensors to an external device using 7m cable&lt;br /&gt;
&lt;br /&gt;
The wire cable color:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;RS232/RS485:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig18-wiring-RS232-RS485.png|frameless|801px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 13. Wiring diagram of RS232 and RS485 output signals&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | RXD/B-&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#3b8dd9;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Blue&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | TXD/A+&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 12-37 VDC or 7.5-75V for SP PRO series&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AF:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig19-wiring-AF.png|frameless|873px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 14. Wiring diagram of Analog and Frequency output signals.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;text-align:center;&amp;quot;&lt;br /&gt;
! Wire Color !! Description&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#000;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Black&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | GND (Ground) (V-)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#f4d03f;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Yellow&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | Out (Analog/Frequency)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;div style=&amp;quot;display:flex;align-items:center;justify-content:center;gap:8px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;display:inline-block;width:32px;height:14px;background:#d63a3a;&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;span style=&amp;quot;min-width:60px;text-align:left;font-weight:bold;&amp;quot;&amp;gt;Red&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt; || style=&amp;quot;text-align:left;&amp;quot; | 15-37 VDC or 7.5-75V for SP PRO series&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Connect the fuse holder to LIGO sensor power cable (brown wire) in close vicinity to the vehicle power supply circuit&lt;br /&gt;
* Installing the fuse in the fuse holder&lt;br /&gt;
&lt;br /&gt;
=== Sealing ===&lt;br /&gt;
&lt;br /&gt;
* Install the bolt through a hole in the seal.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-SP-fig22-sealing.png|frameless|891px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 15. Sealing&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&lt;br /&gt;
=== Connecting sensor to smartphone and setting ===&lt;br /&gt;
&lt;br /&gt;
To configure the LIGO AIR fuel sensor, download the &#039;&#039;&#039;LIGO BLE Configurator&#039;&#039;&#039; app on &#039;&#039;&#039;Appstore (for IOS) or CH Play (for Android devices).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Hereinafter LIGO BLE Configurator is referred to as the &amp;quot;application&amp;quot; or &amp;quot;app&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; For full operation, you must grant all the permissions requested by the app&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-AIR-fig9-app-permissions-1.jpg|Figure 16. Grant all the permissions request by program&lt;br /&gt;
File:LIGO-AIR-fig10-app-permissions-2.jpg|Figure 17. Grant all the permissions request by program&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Grant all the permissions request by program as above&lt;br /&gt;
&lt;br /&gt;
==== Connecting sensor ====&lt;br /&gt;
&lt;br /&gt;
Run the application and select the sensor you want to connect to, as shown in the pictures below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;gallery mode=&amp;quot;packed&amp;quot; widths=&amp;quot;250&amp;quot; heights=&amp;quot;500&amp;quot;&amp;gt;&lt;br /&gt;
File:LIGO-Connect-PRO-BLE-1.jpg|Figure 18. Selecting sensor type&lt;br /&gt;
File:LIGO-Connect-PRO-BLE-2.jpg|Figure 19. Connecting sensor&lt;br /&gt;
&amp;lt;/gallery&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Set Empty and Set Full ====&lt;br /&gt;
&lt;br /&gt;
Step 1: Set Full&lt;br /&gt;
&lt;br /&gt;
* Immerse the sensor in fuel until the fuel reaches the sensor flange&lt;br /&gt;
* Wait for C_working value to stablize and then press Set Full button&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig13-set-full.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 20. Set Full&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Step 2: Set Empty&lt;br /&gt;
&lt;br /&gt;
* Remove the sensor from the fuel and wait for 1-2 minutes allow the fuel drain from the measuring probe and then press Set Empty button.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;text-align:center;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:LIGO-AIR-fig14-set-empty.jpg|frameless|304px]]&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-style:italic; color:#555;&amp;quot;&amp;gt;Figure 21. Set Empty&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Calibration table ===&lt;br /&gt;
&lt;br /&gt;
After installation, the sensor must be calibrated to ensure accurate volume measurement. Calibration table can be performed either on the platform (server) or locally on the sensor.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Platform (Server):&#039;&#039;&#039; The sensor sends raw level values (ranging from 0 to 4095) to the server via the telematic device. The platform uses a calibration table to convert these values into volume (liters).&lt;br /&gt;
* &#039;&#039;&#039;Calibration table on the Sensor:&#039;&#039;&#039; The sensor directly sends volume values (liters) based on the internal calibration table.&lt;br /&gt;
&lt;br /&gt;
Note: For LIGO AIR sensors, the manufacturer recommends performing calibration table on the platform.&lt;br /&gt;
&lt;br /&gt;
== Operation ==&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
&lt;br /&gt;
== Maintenance ==&lt;br /&gt;
&lt;br /&gt;
== Certification / Compliance ==&lt;br /&gt;
&lt;br /&gt;
Refer to [[LIGO PRO 2 Certification &amp;amp; Approvals]] for current certificate status.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Support / Contact ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Channel !! Detail&lt;br /&gt;
|-&lt;br /&gt;
| Company || SOJI ELECTRONICS.,JSC&lt;br /&gt;
|-&lt;br /&gt;
| Address || C50-LK13, Lot No. 09, New Urban Area, Le Trong Tan street, Duong Noi Ward, Ha Dong District, Hanoi City, Vietnam&lt;br /&gt;
|-&lt;br /&gt;
| Tel/Fax || +84 24 62 932 369&lt;br /&gt;
|-&lt;br /&gt;
| Email || contact@sojielectronics.com&lt;br /&gt;
|-&lt;br /&gt;
| Website || [http://sojielectronics.com sojielectronics.com]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div class=&amp;quot;noprint&amp;quot;&amp;gt;&lt;br /&gt;
== Related Documents ==&lt;br /&gt;
&#039;&#039;Cross-references to other documents.&#039;&#039;&lt;br /&gt;
{{RelatedDocuments}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Revision History ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;revision-history-auto&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;span class=&amp;quot;dim&amp;quot;&amp;gt;Loading revision history...&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2 ]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_2_User_Guide&amp;diff=2048</id>
		<title>Category:LIGO PRO 2 User Guide</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=Category:LIGO_PRO_2_User_Guide&amp;diff=2048"/>
		<updated>2026-06-24T10:44:14Z</updated>

		<summary type="html">&lt;p&gt;Admin: Auto-created category for LIGO PRO 2 User Guide&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- auto-created category --&amp;gt;&lt;br /&gt;
{{DEFAULTSORT:05 LIGO PRO 2 User Guide}}&lt;br /&gt;
&lt;br /&gt;
[[Category:LIGO PRO 2]]&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>