All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.diozero.devices.sandpit.VL6180 Maven / Gradle / Ivy

The newest version!
package com.diozero.devices.sandpit;

/*-
 * #%L
 * Organisation: diozero
 * Project:      diozero - Core
 * Filename:     VL6180.java
 * 
 * This file is part of the diozero project. More information about this project
 * can be found at https://www.diozero.com/.
 * %%
 * Copyright (C) 2016 - 2024 diozero
 * %%
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * #L%
 */

import java.nio.ByteOrder;
import java.util.Date;
import java.util.GregorianCalendar;

import org.tinylog.Logger;

import com.diozero.api.I2CDevice;
import com.diozero.api.I2CDeviceInterface.I2CMessage;
import com.diozero.api.RuntimeIOException;
import com.diozero.devices.DistanceSensorInterface;
import com.diozero.util.SleepUtil;

/**
 * References:
 * 
 */
public class VL6180 implements DistanceSensorInterface {
	// Register addresses
	// Identification
	private static final int IDENTIFICATION_MODEL_ID = 0x0000;
	private static final int IDENTIFICATION_MODEL_REV_MAJOR = 0x0001;
	private static final int IDENTIFICATION_MODEL_REV_MINOR = 0x0002;
	private static final int IDENTIFICATION_MODULE_REV_MAJOR = 0x0003;
	private static final int IDENTIFICATION_MODULE_REV_MINOR = 0x0004;
	private static final int IDENTIFICATION_DATE = 0x0006; // 16-bit value
	private static final int IDENTIFICATION_TIME = 0x0008; // 16-bit value
	// System setup
	private static final int SYSTEM_MODE_GPIO0 = 0x0010;
	private static final int SYSTEM_MODE_GPIO1 = 0x0011;
	private static final int SYSTEM_HISTORY_CTRL = 0x0012;
	private static final int SYSTEM_INTERRUPT_CONFIG_GPIO = 0x0014;
	private static final int SYSTEM_INTERRUPT_CLEAR = 0x0015;
	private static final int SYSTEM_FRESH_OUT_OF_RESET = 0x0016;
	private static final int SYSTEM_GROUPED_PARAMETER_HOLD = 0x0017;
	// Range setup
	private static final int SYSRANGE_START = 0x0018;
	private static final int SYSRANGE_THRESH_HIGH = 0x0019;
	private static final int SYSRANGE_THRESH_LOW = 0x001A;
	private static final int SYSRANGE_INTERMEASUREMENT_PERIOD = 0x001B;
	private static final int SYSRANGE_MAX_CONVERGENCE_TIME = 0x001C;
	private static final int SYSRANGE_CROSSTALK_COMPENSATION_RATE = 0x001E;
	private static final int SYSRANGE_CROSSTALK_VALID_HEIGHT = 0x0021;
	private static final int SYSRANGE_EARLY_CONVERGENCE_ESTIMATE = 0x0022;
	private static final int SYSRANGE_PART_TO_PART_RANGE_OFFSET = 0x0024;
	private static final int SYSRANGE_RANGE_IGNORE_VALID_HEIGHT = 0x0025;
	private static final int SYSRANGE_RANGE_IGNORE_THRESHOLD = 0x0026;
	private static final int SYSRANGE_MAX_AMBIENT_LEVEL_MULT = 0x002C;
	private static final int SYSRANGE_RANGE_CHECK_ENABLES = 0x002D;
	private static final int SYSRANGE_VHV_RECALIBRATE = 0x002E;
	private static final int SYSRANGE_VHV_REPEAT_RATE = 0x0031;
	// Ambient Light Sensor
	private static final int SYSALS_START = 0x0038;
	private static final int SYSALS_THRESH_HIGH = 0x003A;
	private static final int SYSALS_THRESH_LOW = 0x003C;
	private static final int SYSALS_INTERMEASUREMENT_PERIOD = 0x003E;
	private static final int SYSALS_ANALOGUE_GAIN = 0x003F;
	private static final int SYSALS_INTEGRATION_PERIOD = 0x0040;
	// Results
	private static final int RESULT_RANGE_STATUS = 0x004D;
	private static final int RESULT_ALS_STATUS = 0x004E;
	private static final int RESULT_INTERRUPT_STATUS_GPIO = 0x004F;
	private static final int RESULT_ALS_VAL = 0x0050;
	private static final int RESULT_HISTORY_BUFFER = 0x0052;
	private static final int RESULT_RANGE_VAL = 0x0062;
	private static final int RESULT_RANGE_RAW = 0x0064;
	private static final int RESULT_RANGE_RETURN_RATE = 0x0066;
	private static final int RESULT_RANGE_REFERENCE_RATE = 0x0068;
	private static final int RESULT_RANGE_RETURN_SIGNAL_COUNT = 0x006C;
	private static final int RESULT_RANGE_REFERENCE_SIGNAL_COUNT = 0x0070;
	private static final int RESULT_RANGE_RETURN_AMB_COUNT = 0x0074;
	private static final int RESULT_RANGE_REFERENCE_AMB_COUNT = 0x0078;
	private static final int RESULT_RANGE_RETURN_CONV_TIME = 0x007C;
	private static final int RESULT_RANGE_REFERENCE_CONV_TIME = 0x0080;

	private static final int READOUT_AVERAGING_SAMPLE_PERIOD = 0x010A;
	private static final int FIRMWARE_BOOTUP = 0x0119;
	private static final int FIRMWARE_RESULT_SCALER = 0x0120;
	private static final int I2C_SLAVE_DEVICE_ADDRESS = 0x0212;
	private static final int INTERLEAVED_MODE_ENABLE = 0x02A3;

	private static final int DEFAULT_ADDRESS = 0x29;
	public static final byte VL6180_MODEL_ID = (byte) 0xb4;

	private I2CDevice device;
	private short modelId;
	private short modelMajor;
	private short modelMinor;
	private short moduleMajor;
	private short moduleMinor;
	private Date manufactureDateTime;
	private int manufacturePhase;

	public VL6180() {
		device = I2CDevice.builder(DEFAULT_ADDRESS).setByteOrder(ByteOrder.BIG_ENDIAN).build();

		init();

		modelId = (short) (readByte(IDENTIFICATION_MODEL_ID) & 0xff);
		modelMajor = (short) (readByte(IDENTIFICATION_MODEL_REV_MAJOR) & 0x07);
		modelMinor = (short) (readByte(IDENTIFICATION_MODEL_REV_MINOR) & 0x07);
		moduleMajor = (short) (readByte(IDENTIFICATION_MODULE_REV_MAJOR) & 0x07);
		moduleMinor = (short) (readByte(IDENTIFICATION_MODULE_REV_MINOR) & 0x07);
		int id_date = readShort(IDENTIFICATION_DATE) & 0xffff;
		int year = (id_date >> 12) & 0xf;
		int month = (id_date >> 8) & 0xf;
		int day_of_month = (id_date >> 3) & 0x1f;
		manufacturePhase = id_date & 0x07;
		int time = (readShort(IDENTIFICATION_TIME) & 0xffff) * 2;
		manufactureDateTime = new GregorianCalendar(2010 + year, month - 1, day_of_month - 1, time / 60 / 60,
				(time / 60) % 60, time % 60).getTime();
	}

	public int getModelId() {
		return modelId;
	}

	public short getModelMajor() {
		return modelMajor;
	}

	public short getModelMinor() {
		return modelMinor;
	}

	public short getModuleMajor() {
		return moduleMajor;
	}

	public short getModuleMinor() {
		return moduleMinor;
	}

	public Date getManufactureDateTime() {
		return manufactureDateTime;
	}

	public int getManufacturePhase() {
		return manufacturePhase;
	}

	private void init() {
		if (readByte(SYSTEM_FRESH_OUT_OF_RESET) == 1) {
			Logger.debug("initialising...");
			// Mandatory : private registers
			writeByte(0x207, 0x01);
			writeByte(0x208, 0x01);
			writeByte(0x096, 0x00);
			writeByte(0x097, 0xFD);
			writeByte(0x0E3, 0x01);
			writeByte(0x0E4, 0x03);
			writeByte(0x0E5, 0x02);
			writeByte(0x0E6, 0x01);
			writeByte(0x0E7, 0x03);
			writeByte(0x0F5, 0x02);
			writeByte(0x0D9, 0x05);
			writeByte(0x0DB, 0xCE);
			writeByte(0x0DC, 0x03);
			writeByte(0x0DD, 0xF8);
			writeByte(0x09F, 0x00);
			writeByte(0x0A3, 0x3C);
			writeByte(0x0B7, 0x00);
			writeByte(0x0BB, 0x3C);
			writeByte(0x0B2, 0x09);
			writeByte(0x0CA, 0x09);
			writeByte(0x198, 0x01);
			writeByte(0x1B0, 0x17);
			writeByte(0x1AD, 0x00);
			writeByte(0x0FF, 0x05);
			writeByte(0x100, 0x05);
			writeByte(0x199, 0x05);
			writeByte(0x1A6, 0x1B);
			writeByte(0x1AC, 0x3E);
			writeByte(0x1A7, 0x1F);
			writeByte(0x030, 0x00);
		}

		writeByte(SYSTEM_FRESH_OUT_OF_RESET, 0);
		SleepUtil.sleepMillis(10);

		// Recommended : Public registers - See data sheet for more detail
		// Enables polling for 'New Sample ready' when measurement completes
		writeByte(SYSTEM_MODE_GPIO1, 0x10);
		// Set the averaging sample period (compromise between lower noise and increased
		// execution time)
		writeByte(READOUT_AVERAGING_SAMPLE_PERIOD, 0x30);
		// Sets the light and dark gain (upper nibble). Dark gain should not be changed.
		writeByte(SYSALS_ANALOGUE_GAIN, 0x46);
		// Sets the # of range measurements after which auto calibration of system is
		// performed
		writeByte(SYSRANGE_VHV_REPEAT_RATE, 0xFF);
		// Set ALS integration time to 100ms
		writeByte(0x0041, 0x63);
		// Perform a single temperature calibration of the ranging sensor
		// TODO Check for completion by waiting for the bit to be cleared
		writeByte(SYSRANGE_VHV_RECALIBRATE, 0x01);
		// Optional: Public registers - See data sheet for more detail
		// Set default ranging inter-measurement period to 100ms
		writeByte(SYSRANGE_INTERMEASUREMENT_PERIOD, 0x09);
		// Set default ALS inter-measurement period to 500ms
		writeByte(SYSALS_INTERMEASUREMENT_PERIOD, 0x31);
		// Configures interrupt on 'New Sample Ready threshold event'
		// writeByte(SYSTEM_INTERRUPT_CONFIG_GPIO, 0x24);
		writeByte(SYSTEM_INTERRUPT_CONFIG_GPIO, 0x04);
	}

	private byte readByte(int register) {
		byte[] buffer = new byte[3];
		I2CMessage[] messages = new I2CMessage[2];
		messages[0] = new I2CMessage(I2CMessage.I2C_M_WR, 2);
		buffer[0] = (byte) ((register >> 8) & 0xff);
		buffer[1] = (byte) (register & 0xff);
		messages[1] = new I2CMessage(I2CMessage.I2C_M_RD, 1);

		device.readWrite(messages, buffer);

		return buffer[2];
	}

	private short readShort(int register) {
		byte[] buffer = new byte[4];
		I2CMessage[] messages = new I2CMessage[2];
		messages[0] = new I2CMessage(I2CMessage.I2C_M_WR, 2);
		buffer[0] = (byte) ((register >> 8) & 0xff);
		buffer[1] = (byte) (register & 0xff);
		messages[1] = new I2CMessage(I2CMessage.I2C_M_RD, 2);

		device.readWrite(messages, buffer);

		return (short) ((buffer[2] << 8) | (buffer[3] & 0xff));
	}

	private void writeByte(int register, int b) {
		/*-
		byte[] buffer = new byte[3];
		I2CMessage[] messages = new I2CMessage[1];
		messages[0] = new I2CMessage(I2CMessage.I2C_M_WR, 3);
		buffer[0] = (byte) ((register >> 8) & 0xff);
		buffer[1] = (byte) (register & 0xff);
		buffer[2] = (byte) b;
		
		device.readWrite(messages, buffer);
		*/

		byte[] buffer = new byte[3];
		buffer[0] = (byte) ((register >> 8) & 0xff);
		buffer[1] = (byte) (register & 0xff);
		buffer[2] = (byte) b;
		device.writeBytes(buffer);
	}

	private void writeShort(int register, int s) {
		/*-
		byte[] buffer = new byte[4];
		I2CMessage[] messages = new I2CMessage[1];
		messages[0] = new I2CMessage(I2CMessage.I2C_M_WR, 4);
		buffer[0] = (byte) ((register >> 8) & 0xff);
		buffer[1] = (byte) (register & 0xff);
		buffer[2] = (byte) ((s >> 8) & 0xff);
		buffer[3] = (byte) (s & 0xff);
		
		device.readWrite(messages, buffer);
		*/

		byte[] buffer = new byte[4];
		buffer[0] = (byte) ((register >> 8) & 0xff);
		buffer[1] = (byte) (register & 0xff);
		buffer[2] = (byte) ((s >> 8) & 0xff);
		buffer[3] = (byte) (s & 0xff);
		device.writeBytes(buffer);
	}

	@Override
	public void close() {
		device.close();
	}

	@Override
	public float getDistanceCm() throws RuntimeIOException {
		writeByte(SYSTEM_INTERRUPT_CLEAR, 0b101);

		// Start single-shot mode
		writeByte(SYSRANGE_START, 0b01);
		byte interrupt_status;
		long start_ms = System.currentTimeMillis();
		do {
			interrupt_status = readByte(RESULT_INTERRUPT_STATUS_GPIO);
		} while ((interrupt_status & 0x04) == 0 && System.currentTimeMillis() - start_ms < 1000);

		return (readByte(RESULT_RANGE_VAL) & 0xff) / 10f;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy