Skip to content

Commit

Permalink
Create Vl6180x implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
torbacz committed Jan 7, 2025
1 parent 1fbea4d commit ede9617
Show file tree
Hide file tree
Showing 17 changed files with 824 additions and 0 deletions.
12 changes: 12 additions & 0 deletions devices/Vl6180X/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("Iot.Device.Vl6180X")]
[assembly: AssemblyCompany("nanoFramework Contributors")]
[assembly: AssemblyCopyright("Copyright(c).NET Foundation and Contributors")]

[assembly: ComVisible(false)]
27 changes: 27 additions & 0 deletions devices/Vl6180X/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Vl6180X - distance sensor

The Vl6180X sensor is a Time-to-Flight sensor measuring precisely distances. The sensor allows you to get precise short distance measurement (from 5 millimeters to 2 meters) as well as long distance measurement (up to 8 meters but with a decreased precision). This sensor is a laser ranging sensor. It is using laser pulses to measure the distances.

## Documentation

**Vl6180X** [datasheet](https://www.st.com/resource/en/datasheet/vl6180x.pdf)


## Usage

**Important**: make sure you properly setup the I2C pins especially for ESP32 before creating the `I2cDevice`, make sure you install the `nanoFramework.Hardware.ESP32 nuget`:

```csharp
// when connecting to an ESP32 device, need to configure the I2C GPIOs used for the bus
Configuration.SetPinFunction(11, DeviceFunction.I2C1_DATA);
Configuration.SetPinFunction(10, DeviceFunction.I2C1_CLOCK);

using VL6180X sensor = new(I2cDevice.Create(new I2cConnectionSettings(1, VL6180X.DefaultI2cAddress)));
sensor.Init();
while (true)
{
var distance = sensor.ReadRange();
Console.WriteLine($"Distance: {distance.Centimeters} cm.");
Thread.Sleep(500);
}
```
73 changes: 73 additions & 0 deletions devices/Vl6180X/RegisterAddresses.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Iot.Device.VL6180X
{
internal enum RegisterAddresses : ushort
{
IDENTIFICATION__MODEL_ID = 0x000,
IDENTIFICATION__MODEL_REV_MAJOR = 0x001,
IDENTIFICATION__MODEL_REV_MINOR = 0x002,
IDENTIFICATION__MODULE_REV_MAJOR = 0x003,
IDENTIFICATION__MODULE_REV_MINOR = 0x004,
IDENTIFICATION__DATE_HI = 0x006,
IDENTIFICATION__DATE_LO = 0x007,
IDENTIFICATION__TIME = 0x008, // 16-bit
SYSTEM__MODE_GPIO0 = 0x010,
SYSTEM__MODE_GPIO1 = 0x011,
SYSTEM__HISTORY_CTRL = 0x012,
SYSTEM__INTERRUPT_CONFIG_GPIO = 0x014,
SYSTEM__INTERRUPT_CLEAR = 0x015,
SYSTEM__FRESH_OUT_OF_RESET = 0x016,
SYSTEM__GROUPED_PARAMETER_HOLD = 0x017,
SYSRANGE__START = 0x018,
SYSRANGE__THRESH_HIGH = 0x019,
SYSRANGE__THRESH_LOW = 0x01A,
SYSRANGE__INTERMEASUREMENT_PERIOD = 0x01B,
SYSRANGE__MAX_CONVERGENCE_TIME = 0x01C,
SYSRANGE__CROSSTALK_COMPENSATION_RATE = 0x01E, // 16-bit
SYSRANGE__CROSSTALK_VALID_HEIGHT = 0x021,
SYSRANGE__EARLY_CONVERGENCE_ESTIMATE = 0x022, // 16-bit
SYSRANGE__PART_TO_PART_RANGE_OFFSET = 0x024,
SYSRANGE__RANGE_IGNORE_VALID_HEIGHT = 0x025,
SYSRANGE__RANGE_IGNORE_THRESHOLD = 0x026, // 16-bit
SYSRANGE__MAX_AMBIENT_LEVEL_MULT = 0x02C,
SYSRANGE__RANGE_CHECK_ENABLES = 0x02D,
SYSRANGE__VHV_RECALIBRATE = 0x02E,
SYSRANGE__VHV_REPEAT_RATE = 0x031,
SYSALS__START = 0x038,
SYSALS__THRESH_HIGH = 0x03A,
SYSALS__THRESH_LOW = 0x03C,
SYSALS__INTERMEASUREMENT_PERIOD = 0x03E,
SYSALS__ANALOGUE_GAIN = 0x03F,
SYSALS__INTEGRATION_PERIOD = 0x040,
RESULT__RANGE_STATUS = 0x04D,
RESULT__ALS_STATUS = 0x04E,
RESULT__INTERRUPT_STATUS_GPIO = 0x04F,
RESULT__ALS_VAL = 0x050, // 16-bit
RESULT__HISTORY_BUFFER_0 = 0x052, // 16-bit
RESULT__HISTORY_BUFFER_1 = 0x054, // 16-bit
RESULT__HISTORY_BUFFER_2 = 0x056, // 16-bit
RESULT__HISTORY_BUFFER_3 = 0x058, // 16-bit
RESULT__HISTORY_BUFFER_4 = 0x05A, // 16-bit
RESULT__HISTORY_BUFFER_5 = 0x05C, // 16-bit
RESULT__HISTORY_BUFFER_6 = 0x05E, // 16-bit
RESULT__HISTORY_BUFFER_7 = 0x060, // 16-bit
RESULT__RANGE_VAL = 0x062,
RESULT__RANGE_RAW = 0x064,
RESULT__RANGE_RETURN_RATE = 0x066, // 16-bit
RESULT__RANGE_REFERENCE_RATE = 0x068, // 16-bit
RESULT__RANGE_RETURN_SIGNAL_COUNT = 0x06C, // 32-bit
RESULT__RANGE_REFERENCE_SIGNAL_COUNT = 0x070, // 32-bit
RESULT__RANGE_RETURN_AMB_COUNT = 0x074, // 32-bit
RESULT__RANGE_REFERENCE_AMB_COUNT = 0x078, // 32-bit
RESULT__RANGE_RETURN_CONV_TIME = 0x07C, // 32-bit
RESULT__RANGE_REFERENCE_CONV_TIME = 0x080, // 32-bit
RANGE_SCALER = 0x096, // 16-bit - see STSW-IMG003 core/inc/vl6180x_def.h
READOUT__AVERAGING_SAMPLE_PERIOD = 0x10A,
FIRMWARE__BOOTUP = 0x119,
FIRMWARE__RESULT_SCALER = 0x120,
I2C_SLAVE__DEVICE_ADDRESS = 0x212,
INTERLEAVED_MODE__ENABLE = 0x2A3,
}
}
112 changes: 112 additions & 0 deletions devices/Vl6180X/Settings.StyleCop
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<StyleCopSettings Version="105">
<Analyzers>
<Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
<Rules>
<Rule Name="FileHeaderMustContainFileName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustHaveValidCompanyText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderFileNameDocumentationMustMatchTypeName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PropertyDocumentationMustHaveValueText">
<RuleSettings>
<BooleanProperty Name="Enabled">True</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustBeginWithACapitalLetter">
<RuleSettings>
<BooleanProperty Name="Enabled">True</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustEndWithAPeriod">
<RuleSettings>
<BooleanProperty Name="Enabled">True</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustShowCopyright">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustHaveCopyrightText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustBeSpelledCorrectly">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustContainWhitespace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings>
<BooleanProperty Name="IgnorePrivates">True</BooleanProperty>
<BooleanProperty Name="IgnoreInternals">True</BooleanProperty>
</AnalyzerSettings>
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
<Rules>
<Rule Name="PrefixLocalCallsWithThis">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PrefixCallsCorrectly">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules">
<Rules>
<Rule Name="UsingDirectivesMustBePlacedWithinNamespace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustAppearInTheCorrectOrder">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustBeOrderedByAccess">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.NamingRules">
<Rules>
<Rule Name="FieldNamesMustNotBeginWithUnderscore">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FieldNamesMustNotUseHungarianNotation">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
</Analyzers>
</StyleCopSettings>
141 changes: 141 additions & 0 deletions devices/Vl6180X/VL6180X.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Buffers.Binary;
using System.Device.I2c;
using System.Diagnostics;
using UnitsNet;

namespace Iot.Device.VL6180X
{
/// <summary>
/// Represents VL6180X.
/// </summary>
public sealed class VL6180X : IDisposable
{
/// <summary>
/// The default I2C Address.
/// </summary>
public const byte DefaultI2cAddress = 0x29;

private readonly bool _shouldDispose;
private I2cDevice _i2cDevice;

/// <summary>
/// Initializes a new instance of the <see cref="VL6180X" /> class.
/// </summary>
/// <param name="i2cDevice">The I2C Device.</param>
/// <param name="shouldDispose">True to dispose the I2C Device at dispose.</param>
public VL6180X(I2cDevice i2cDevice, bool shouldDispose = true)
{
_i2cDevice = i2cDevice ?? throw new ArgumentNullException(nameof(i2cDevice));
_shouldDispose = shouldDispose;
}

/// <summary>
/// Initialization of the sensor, include a long sequence of writing
/// which is coming from the offical API with no more information on the
/// registers and their functions. Few can be reversed engineer based on
/// other functions but not all.
/// </summary>
public void Init()
{
var alreadyInitialized = ReadRegister(RegisterAddresses.SYSTEM__FRESH_OUT_OF_RESET);
if (alreadyInitialized != 1)
{
return;
}

WriteRegister(0x207, 0x01);
WriteRegister(0x208, 0x01);
WriteRegister(0x096, 0x00);
WriteRegister(0x097, 0xFD); // RANGE_SCALER = 253
WriteRegister(0x0E3, 0x01);
WriteRegister(0x0E4, 0x03);
WriteRegister(0x0E5, 0x02);
WriteRegister(0x0E6, 0x01);
WriteRegister(0x0E7, 0x03);
WriteRegister(0x0F5, 0x02);
WriteRegister(0x0D9, 0x05);
WriteRegister(0x0DB, 0xCE);
WriteRegister(0x0DC, 0x03);
WriteRegister(0x0DD, 0xF8);
WriteRegister(0x09F, 0x00);
WriteRegister(0x0A3, 0x3C);
WriteRegister(0x0B7, 0x00);
WriteRegister(0x0BB, 0x3C);
WriteRegister(0x0B2, 0x09);
WriteRegister(0x0CA, 0x09);
WriteRegister(0x198, 0x01);
WriteRegister(0x1B0, 0x17);
WriteRegister(0x1AD, 0x00);
WriteRegister(0x0FF, 0x05);
WriteRegister(0x100, 0x05);
WriteRegister(0x199, 0x05);
WriteRegister(0x1A6, 0x1B);
WriteRegister(0x1AC, 0x3E);
WriteRegister(0x1A7, 0x1F);
WriteRegister(0x030, 0x00);
WriteRegister(RegisterAddresses.SYSTEM__FRESH_OUT_OF_RESET, 0);
}

/// <summary>
/// Reads the range measurement value from the sensor.
/// </summary>
/// <returns>The measured range value.</returns>
public Length ReadRange()
{
WriteRegister(RegisterAddresses.SYSRANGE__START, 0x01);
var range = ReadRegister(RegisterAddresses.RESULT__RANGE_VAL);
WriteRegister(RegisterAddresses.SYSTEM__INTERRUPT_CLEAR, 0x01);
return Length.FromMillimeters(range);
}

/// <inheritdoc/>
public void Dispose()
{
if (_shouldDispose)
{
_i2cDevice?.Dispose();
_i2cDevice = null;
}
}

private void WriteRegister(RegisterAddresses reg, byte param)
{
WriteRegister((ushort)reg, param);
}

private void WriteRegister(ushort reg, byte param)
{
var buffer = new byte[3];
BinaryPrimitives.WriteUInt16BigEndian(buffer, reg);
buffer[2] = param;
Debug.WriteLine($"Writing to register 0x{reg:X4}: 0x{param:X2}");

var result = _i2cDevice.Write(buffer);
if (result.Status != I2cTransferStatus.FullTransfer)
{
throw new InvalidOperationException("I2C write failed");
}
}

private byte ReadRegister(RegisterAddresses reg)
{
var writeBuffer = new byte[2];
var readBuffer = new byte[1];
BinaryPrimitives.WriteUInt16BigEndian(writeBuffer, (ushort)reg);
Debug.WriteLine($"Writing register address: 0x{writeBuffer[0]:X2} 0x{writeBuffer[1]:X2}");

var result = _i2cDevice.WriteRead(writeBuffer, readBuffer);
if (result.Status != I2cTransferStatus.FullTransfer)
{
throw new InvalidOperationException("I2C write failed while setting register address");
}

Debug.WriteLine($"Read data from register 0x{reg:X4}: 0x{readBuffer[0]:X2}");
return readBuffer[0];
}
}
}
Loading

0 comments on commit ede9617

Please sign in to comment.