<?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-07-22T19:04:20Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.45.3</generator>
	<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>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=LIGO_PRO_2_User_Guide&amp;diff=2047</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=2047"/>
		<updated>2026-06-24T10:44:13Z</updated>

		<summary type="html">&lt;p&gt;Admin: Create page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Safety Information ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;This section documents all safety-related information per IEC/IEEE 82079-1 and ANSI Z535.6 standards.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* General safety requirements (PPE, qualified personnel, working conditions)&lt;br /&gt;
* Specific hazards using callouts:&lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Danger|title=...|body=...}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — imminent hazard, death/serious injury&lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Warning|title=...|body=...}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — potential hazard, possible injury&lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Caution|title=...|body=...}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — potential equipment damage&lt;br /&gt;
** &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;{{Notice|title=...|body=...}}&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; — important info, no hazard&lt;br /&gt;
* Emergency procedures (fire, electric shock, chemical exposure, evacuation)&lt;br /&gt;
* Emergency contact numbers&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example callout:&#039;&#039;&#039;&lt;br /&gt;
{{Notice|title=Sample Notice|body=Read entire guide before installation. Qualified technicians only. Wear PPE.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&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;Introduce the product: what it is, what it does, and key value proposition.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039;&lt;br /&gt;
* 1–2 paragraph product description: name, category, primary function, key features&lt;br /&gt;
* &#039;&#039;&#039;Intended Use&#039;&#039;&#039; — bullet list of applications the product is designed for&lt;br /&gt;
* &#039;&#039;&#039;NOT Intended For&#039;&#039;&#039; — bullet list of excluded applications (safety/regulatory)&lt;br /&gt;
* &#039;&#039;&#039;Product Variants&#039;&#039;&#039; — table of variants if multiple SKUs exist (Variant / Description / Use Case)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example structure:&#039;&#039;&#039;&lt;br /&gt;
* Paragraph: &amp;quot;The [Product Name] is a [type] device designed for [primary application]...&amp;quot;&lt;br /&gt;
* Variants table with columns: Variant | Description | Use Case&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Package Contents ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;List everything included in the shipment box.&#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;No.&#039;&#039;&#039; — item number (1, 2, 3, ...)&lt;br /&gt;
* &#039;&#039;&#039;Item&#039;&#039;&#039; — item name (vd: &amp;quot;Main sensor unit&amp;quot;, &amp;quot;Mounting bracket&amp;quot;, &amp;quot;Connection cable 2m&amp;quot;)&lt;br /&gt;
* &#039;&#039;&#039;Quantity&#039;&#039;&#039; — quantity per package (vd: 01, 02)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note to include:&#039;&#039;&#039; &amp;quot;Verify all items are present before installation. Contact support if any item is missing or damaged.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Specifications ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Technical specifications of 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; specification table with columns Parameter / Value / Unit, covering:&lt;br /&gt;
* Mechanical: Dimensions (L × W × H), Weight&lt;br /&gt;
* Environmental: Operating temperature, Storage temperature, Ingress protection (IP)&lt;br /&gt;
* Electrical: Power supply, Current consumption (avg/peak/sleep)&lt;br /&gt;
* Wireless (if applicable): Bluetooth/WiFi/LoRa protocol&lt;br /&gt;
* Service life (expected years)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Sub-sections to consider:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Technical Specifications&#039;&#039;&#039; — main spec table&lt;br /&gt;
* &#039;&#039;&#039;Dimensions&#039;&#039;&#039; — mechanical drawing image (upload as &amp;quot;Product-dimensions.png&amp;quot;)&lt;br /&gt;
* &#039;&#039;&#039;Pinout/Connector&#039;&#039;&#039; (if applicable)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Step-by-step installation procedure.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Pre-requisite:&#039;&#039;&#039; &amp;quot;Before installation: Read [[#Safety Information|Safety Information]] section.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in (sub-sections):&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Tools Required&#039;&#039;&#039; — bullet list of tools + PPE&lt;br /&gt;
* &#039;&#039;&#039;Step 1: Site Preparation&#039;&#039;&#039; — clean mounting surface, clearance, power off&lt;br /&gt;
* &#039;&#039;&#039;Step 2: Mounting&#039;&#039;&#039; — physical mounting procedure with torque values&lt;br /&gt;
* &#039;&#039;&#039;Step 3: Connection&#039;&#039;&#039; — electrical/wireless connection with wire color codes&lt;br /&gt;
* &#039;&#039;&#039;Step 4: Verification&#039;&#039;&#039; — how to verify installation (LED status, app discovery)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Optional:&#039;&#039;&#039; mounting diagram image (upload as &amp;quot;Product-mounting.png&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Software setup after physical installation.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in (sub-sections):&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Mobile Application&#039;&#039;&#039; — iOS/Android app download links + required permissions&lt;br /&gt;
* &#039;&#039;&#039;Connecting Sensor&#039;&#039;&#039; — numbered steps to discover and pair device&lt;br /&gt;
* &#039;&#039;&#039;Calibration&#039;&#039;&#039; — calibration procedure (Set Full, Set Empty, etc.)&lt;br /&gt;
* &#039;&#039;&#039;Configurable Parameters&#039;&#039;&#039; — table with columns: Parameter / Default / Range / Description&lt;br /&gt;
** Common params: Device name, Measurement interval, Output protocol, Password&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Operation ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Day-to-day usage instructions for end users.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in (sub-sections):&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Reading Data&#039;&#039;&#039; — how data is delivered (gateway, app, cloud)&lt;br /&gt;
* &#039;&#039;&#039;Calibration Methods&#039;&#039;&#039; — table of available calibration methods with recommended option&lt;br /&gt;
* &#039;&#039;&#039;Long-Range Operation&#039;&#039;&#039; — range settings for challenging environments&lt;br /&gt;
* &#039;&#039;&#039;Protective Accessories&#039;&#039;&#039; — optional caps/housings if applicable&lt;br /&gt;
* &#039;&#039;&#039;Operating Modes&#039;&#039;&#039; — normal/diagnostic/sleep modes if applicable&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;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;
&lt;br /&gt;
&#039;&#039;Common problems and solutions for end users.&#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;Symptom&#039;&#039;&#039; — what user observes (vd: &amp;quot;Device does not power on&amp;quot;, &amp;quot;No Bluetooth connection&amp;quot;)&lt;br /&gt;
* &#039;&#039;&#039;Possible Cause&#039;&#039;&#039; — bullet list of potential causes&lt;br /&gt;
* &#039;&#039;&#039;Solution&#039;&#039;&#039; — bullet list of step-by-step solutions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Closing note:&#039;&#039;&#039; &amp;quot;If problem persists, contact [[#Support / Contact|Support]].&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Maintenance ==&lt;br /&gt;
&amp;lt;div class=&amp;quot;dim&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Periodic maintenance and end-of-life procedures.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in (sub-sections):&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Periodic Inspection&#039;&#039;&#039; — table: Frequency (Monthly/Quarterly/Annually/5-year/8-10 year) / Action&lt;br /&gt;
* &#039;&#039;&#039;Cleaning&#039;&#039;&#039; — procedure with prohibited materials (solvents, abrasives)&lt;br /&gt;
* &#039;&#039;&#039;Battery Replacement&#039;&#039;&#039; (if applicable) — procedure + safety warnings&lt;br /&gt;
* &#039;&#039;&#039;Calibration Verification&#039;&#039;&#039; — re-calibration after maintenance&lt;br /&gt;
* &#039;&#039;&#039;Disposal / End-of-Life&#039;&#039;&#039; — recycling instructions per WEEE/RoHS&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Required safety callout:&#039;&#039;&#039;&lt;br /&gt;
{{Warning|title=Battery Replacement Safety|body=Replace only with authorized part. Disconnect before opening. Wear chemical-resistant gloves. Observe correct polarity.}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Certification / Compliance ==&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 with tables):&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;Wireless / Radio&#039;&#039;&#039; — Region / Standard / Status (CE RED, FCC Part 15, ISED RSS, UKCA)&lt;br /&gt;
* &#039;&#039;&#039;Environmental&#039;&#039;&#039; — Standard / Status (RoHS 3, REACH, WEEE, Battery WEEE)&lt;br /&gt;
* &#039;&#039;&#039;Mechanical&#039;&#039;&#039; — Standard / Rating (IEC 60529 IP rating)&lt;br /&gt;
* &#039;&#039;&#039;Bluetooth Qualification&#039;&#039;&#039; (if applicable) — QDID, SIG Member ID, EPDID&lt;br /&gt;
* &#039;&#039;&#039;Warranty&#039;&#039;&#039; — period, exclusions, void conditions&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cross-references:&#039;&#039;&#039;&lt;br /&gt;
* See [[Product Certification &amp;amp; Approvals]] for detailed certificates&lt;br /&gt;
* See [[Product Memberships]] for SIG memberships&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;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.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;What to fill in:&#039;&#039;&#039; contact table with rows:&lt;br /&gt;
* &#039;&#039;&#039;Website&#039;&#039;&#039; — sojielectronics.com&lt;br /&gt;
* &#039;&#039;&#039;Email&#039;&#039;&#039; — support@sojielectronics.com&lt;br /&gt;
* &#039;&#039;&#039;Phone&#039;&#039;&#039; — support number&lt;br /&gt;
* &#039;&#039;&#039;Address&#039;&#039;&#039; — full company address&lt;br /&gt;
* &#039;&#039;&#039;Service hours&#039;&#039;&#039; — vd: &amp;quot;Mon–Fri, 08:00–17:00 GMT+7&amp;quot;&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_BLE_User_Guide&amp;diff=2043</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=2043"/>
		<updated>2026-06-24T04:54:38Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Installing and connecting */&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: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:#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/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; | 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;
=== 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_User_Guide&amp;diff=2042</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=2042"/>
		<updated>2026-06-24T04:23:09Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Configuration */&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: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;
=== 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_AIR_User_Guide&amp;diff=2039</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=2039"/>
		<updated>2026-06-24T04:17:48Z</updated>

		<summary type="html">&lt;p&gt;Admin: Update&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 and 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;2&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;2&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;2&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 || 19.5 dBm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Advertising Protocol || colspan=&amp;quot;2&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 * 3600mAh&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Current consumption in working mode (uA) || 9 || 27&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measurement interval (second) || colspan=&amp;quot;2&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 || 10&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;2&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature (°C) || colspan=&amp;quot;2&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution (bit) || colspan=&amp;quot;2&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Accelerometer || LxS N/A, LxT 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;2&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;2&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;2&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;2&amp;quot; | 10 years&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Storage condition || colspan=&amp;quot;2&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_User_Guide&amp;diff=2038</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=2038"/>
		<updated>2026-06-24T04:16:40Z</updated>

		<summary type="html">&lt;p&gt;Admin: /* Installation */&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;
=== &#039;&#039;&#039;Technical specifications&#039;&#039;&#039; ===&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 and 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;2&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;2&amp;quot; | ± 0.5 %&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Bluetooth specs || colspan=&amp;quot;2&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 || 19.5 dBm&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Advertising Protocol || colspan=&amp;quot;2&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 * 3600mAh&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Current consumption in working mode (uA) || 9 || 27&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Measurement interval (second) || colspan=&amp;quot;2&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 || 10&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Waterproof standard (Ingress protection rating, IP) || colspan=&amp;quot;2&amp;quot; | IP69K&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Operating temperature (°C) || colspan=&amp;quot;2&amp;quot; | -40...+85&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Resolution (bit) || colspan=&amp;quot;2&amp;quot; | 12&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Accelerometer || LxS N/A, LxT 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;2&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;2&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;2&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;2&amp;quot; | 10 years&lt;br /&gt;
|-&lt;br /&gt;
| style=&amp;quot;text-align:left;&amp;quot; | Storage condition || colspan=&amp;quot;2&amp;quot; | 15°C to 30°C, RH &amp;lt; 60%&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== &#039;&#039;&#039;Overview of sensor structure&#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-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;
=== &#039;&#039;&#039;Connecting sensor to smartphone and setting&#039;&#039;&#039; ===&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;
==== &#039;&#039;&#039;Connecting sensor&#039;&#039;&#039; ====&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;
==== &#039;&#039;&#039;Set Empty and Set Full&#039;&#039;&#039; ====&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=Main_Page&amp;diff=2037</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=Main_Page&amp;diff=2037"/>
		<updated>2026-06-24T04:13:26Z</updated>

		<summary type="html">&lt;p&gt;Admin: Create page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&#039;&#039;&#039;Welcome to the SOJI Electronics Wiki!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Find technical resources, product documentation, configuration guides, and compatibility information for all SOJI Electronics products below, or use the search bar to find specific information.&lt;br /&gt;
&amp;lt;div class=&amp;quot;main-page-products-grid&amp;quot;&amp;gt;&lt;br /&gt;
{{#dpl:&lt;br /&gt;
|category=Products&lt;br /&gt;
|namespace=main&lt;br /&gt;
|mode=userformat&lt;br /&gt;
|format=,{{MainPageProductCard{{!}}name=%PAGE%}},,&lt;br /&gt;
|ordermethod=sortkey&lt;br /&gt;
|order=ascending&lt;br /&gt;
|allowcachedresults=false&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;div class=&amp;quot;categories-list categories-list-3&amp;quot;&amp;gt;&lt;br /&gt;
{{CategoryBlock&lt;br /&gt;
| id    = app&lt;br /&gt;
| link  = Software &amp;amp; Applications&lt;br /&gt;
| icon  = Icon-software.svg&lt;br /&gt;
| title = Software &amp;amp; Applications&lt;br /&gt;
| desc  = Mobile applications, configurator tools, and integration software for managing SOJI devices.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{CategoryBlock&lt;br /&gt;
| id    = accessories&lt;br /&gt;
| link  = Accessories&lt;br /&gt;
| icon  = Icon-accessories.svg&lt;br /&gt;
| title = Accessories&lt;br /&gt;
| desc  = Adapters, cables, mounting kits, and signal boosters for SOJI sensor systems.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{CategoryBlock&lt;br /&gt;
| id    = faq&lt;br /&gt;
| link  = Frequently Asked Questions - FAQ&lt;br /&gt;
| icon  = Icon-faq.svg&lt;br /&gt;
| title = FAQ&lt;br /&gt;
| desc  = Common questions about installation, configuration, troubleshooting, and product compatibility.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://wiki.sojielectronics.com/index.php?title=Main_Page&amp;diff=2036</id>
		<title>Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.sojielectronics.com/index.php?title=Main_Page&amp;diff=2036"/>
		<updated>2026-06-24T04:08:21Z</updated>

		<summary type="html">&lt;p&gt;Admin: Update&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
&#039;&#039;&#039;Welcome to the SOJI Electronics Wiki!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Find technical resources, product documentation, configuration guides, and compatibility information for all SOJI Electronics products below, or use the search bar to find specific information.&lt;br /&gt;
&amp;lt;div class=&amp;quot;main-page-products-grid&amp;quot;&amp;gt;&lt;br /&gt;
{{#dpl:&lt;br /&gt;
|category=Products&lt;br /&gt;
|namespace=main&lt;br /&gt;
|mode=userformat&lt;br /&gt;
|format=,{{MainPageProductCard{{!}}name=%PAGE%}},,&lt;br /&gt;
|ordermethod=categorysortkey&lt;br /&gt;
|order=ascending&lt;br /&gt;
|allowcachedresults=false&lt;br /&gt;
}}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;div class=&amp;quot;categories-list categories-list-3&amp;quot;&amp;gt;&lt;br /&gt;
{{CategoryBlock&lt;br /&gt;
| id    = app&lt;br /&gt;
| link  = Software &amp;amp; Applications&lt;br /&gt;
| icon  = Icon-software.svg&lt;br /&gt;
| title = Software &amp;amp; Applications&lt;br /&gt;
| desc  = Mobile applications, configurator tools, and integration software for managing SOJI devices.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{CategoryBlock&lt;br /&gt;
| id    = accessories&lt;br /&gt;
| link  = Accessories&lt;br /&gt;
| icon  = Icon-accessories.svg&lt;br /&gt;
| title = Accessories&lt;br /&gt;
| desc  = Adapters, cables, mounting kits, and signal boosters for SOJI sensor systems.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{CategoryBlock&lt;br /&gt;
| id    = faq&lt;br /&gt;
| link  = Frequently Asked Questions - FAQ&lt;br /&gt;
| icon  = Icon-faq.svg&lt;br /&gt;
| title = FAQ&lt;br /&gt;
| desc  = Common questions about installation, configuration, troubleshooting, and product compatibility.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>