library for veml6075

master
Stefan Haslinger 2022-03-18 15:57:16 +01:00
parent 07a44d3ff0
commit 5459f0f9e3
10 changed files with 273 additions and 0 deletions

3
veml6075/.formatter.exs Normal file
View File

@ -0,0 +1,3 @@
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

27
veml6075/.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
# The directory Mix will write compiled artifacts to.
/_build/
# If you run "mix test --cover", coverage assets end up here.
/cover/
# The directory Mix downloads your dependencies sources to.
/deps/
# Where third-party dependencies like ExDoc output generated docs.
/doc/
# Ignore .fetch files in case you like to edit your project deps locally.
/.fetch
# If the VM crashes, it generates a dump, let's ignore it too.
erl_crash.dump
# Also ignore archive artifacts (built via "mix archive.build").
*.ez
# Ignore package tarball (built via "mix hex.build").
veml6075-*.tar
# Temporary files for e.g. tests
/tmp

21
veml6075/README.md Normal file
View File

@ -0,0 +1,21 @@
# Veml6075
**TODO: Add description**
## Installation
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
by adding `veml6075` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:veml6075, "~> 0.1.0"}
]
end
```
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
be found at <https://hexdocs.pm/veml6075>.

74
veml6075/lib/veml6075.ex Normal file
View File

@ -0,0 +1,74 @@
defmodule VEML6075 do
use GenServer
require Logger
alias VEML6075.{Comm, Config}
def start_link(options \\ %{}) do
GenServer.start_link(__MODULE__, options, name: __MODULE__)
end
def get_measurement do
GenServer.call(__MODULE__, :get_measurement)
end
@impl true
def init(%{address: address, i2c_bus_name: bus_name} = args) do
i2c = Comm.open(bus_name)
config =
args
|> Map.take([:gain, :int_time, :shutdown, :interrupt])
|> Config.new()
Comm.write_config(config, i2c, address)
:timer.send_interval(1_000, :measure)
state = %{
i2c: i2c,
address: address,
config: config,
last_reading: :no_reading
}
{:ok, state}
end
def init(args) do
{bus_name, address} = Comm.discover()
transport = "bus: #{bus_name}, address: #{address}"
Logger.info("Starting VEML6075. Please specify an address and a bus.")
Logger.info("Starting on " <> transport)
defaults =
args
|> Map.put(:address, address)
|> Map.put(:i2c_bus_name, bus_name)
init(defaults)
end
@impl true
def handle_info(
:measure,
%{i2c: i2c, address: address, config: config} = state
) do
last_reading_uva = Comm.readuva(i2c, address, config)
last_reading_uvb = Comm.readuvb(i2c, address, config)
updated_with_reading = %{
state
| last_reading_uva: last_reading_uva,
last_reading_uvb: last_reading_uvb
}
{:noreply, updated_with_reading}
end
@impl true
def handle_call(:get_measurement, _from, state) do
{:reply, state.last_reading, state}
end
end

View File

@ -0,0 +1,32 @@
defmodule VEML6075.Comm do
alias Circuits.I2C
alias VEML6075.Config
@light_register_uva <<7>>
@light_register_uvb <<9>>
@visible_compensation <<a>>
@ir_compensation <<b>>
def discover(possible_addresses \\ [0x10, 0x48]) do
I2C.discover_one!(possible_addresses)
end
def open(bus_name) do
{:ok, i2c} = I2C.open(bus_name)
i2c
end
def write_config(configuration, i2c, sensor) do
command = Config.to_integer(configuration)
I2C.write(i2c, sensor, <<0, command::little-16>>)
end
def read(i2c, sensor, configuration) do
<<uva_raw::little-16>> = I2C.write_read!(i2c, sensor, @uva_register, 2)
<<uvb_raw::little-16>> = I2C.write_read!(i2c, sensor, @uvb_register, 2)
<<visible_compensation::little-16>> = I2C.write_read!(i2c, sensor, @uvb_register, 2)
<<ir_compensation::little-16>> = I2C.write_read!(i2c, sensor, @uvb_register, 2)
Config.convert(configuration, uva_raw, uvb_raw, visible_compensation, ir_compensation)
end
end

View File

@ -0,0 +1,76 @@
defmodule VEML6075.Config do
defstruct uv_it: :it_100_ms,
dynamic: false,
uv_trig: false,
uv_av: false,
shutdown: false
def new, do: struct(__MODULE__)
def new(opts), do: struct(__MODULE__, opts)
def to_integer(config) do
reserved = 0
<<integer::16>> = <<
reserved::9,
uv_it(config.uv_it):3,
high_dynamic(config.dynamic):1,
uv_trig(config,uv_trig):1,
uv_av(config.uv_av):1,
shutdown(config.shutdown)::1
>>
integer
end
defp uv_it(:it_50_ms), do: 0b000
defp uv_it(:it_100_ms), do: 0b001
defp uv_it(:it_200_ms), do: 0b010
defp uv_it(:it_400_ms), do: 0b011
defp uv_it(:it_800_ms), do: 0b100
# Low dynamic: false, High dynamic: true
defp high_dynamic(true), do: 1
defp high_dynamic(_), do: 0
# No active force mode trigger: false, Trigger one measurement: true
# With true, the sensor conducts one measurement every time, the host writes uv_trig = true, returns to 0 automatically
defp uv_trig(true), do: 1
defp uv_trig(_), do: 0
# Activive force mode disable (normal): false, active force mode enable: true
defp uv_av(true), do: 1
defp uv_av(_), do: 0
defp shutdown(true), do: 1
defp shutdown(_), do: 0
calibration_alpha_vis = 1.0;
calibration_beta_vis = 1.0;
calibration_gamma_ir = 1.0;
calibration_delta_ir = 1.0;
uva_vis_coef_a = 2.22
uva_ir_coef_b = 1.33
uvb_vis_coef_c = 2.95
uvb_ir_coef_d = 1.75
uva_responsibility = 0.00110
uvb_responsibility = 0.00125
def convert(uva_raw, uvb_raw, visible_comp, ir_comp) do
# Calculate the simple UVIA and UVIB. These are used to calculate the UVI signal.
uvia = uva_raw - ((uva_vis_coef_a * calibration_alpha_vis * visible_comp) / calibration_gamma_ir)
- ((uva_ir_coef_b * calibration_alpha_vis * ir_comp) / calibration_delta_ir)
uvib = uvb_raw - ((uvb_vis_coef_c * calibration_beta_vis * visible_comp) / calibration_gamma_ir)
- ((uvb_ir_coef_d * calibration_beta_vis * ir_comp) / calibration_delta_ir)
# Convert raw UVIA and UVIB to values scaled by the sensor responsivity
uvia_scaled = uvia * (1.0 / calibration_alpha_vis) * uva_responsibility
uvib_scaled = uvib * (1.0 / calibration_beta_vis) * uvb_responsibility
# Use UVIA and UVIB to calculate the average UVI:
uvi = (uvia_scaled + uvib_scaled) / 2.0;
{uvia, uvib, uvi}
end
end

27
veml6075/mix.exs Normal file
View File

@ -0,0 +1,27 @@
defmodule Veml6075.MixProject do
use Mix.Project
def project do
[
app: :veml6075,
version: "0.1.0",
elixir: "~> 1.13",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger]
]
end
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:circuits_i2c, "~> 1.0.1"}
]
end
end

4
veml6075/mix.lock Normal file
View File

@ -0,0 +1,4 @@
%{
"circuits_i2c": {:hex, :circuits_i2c, "1.0.1", "3a820dfa04bc5a92d0ec3c572cacb1a665802b2913fbc25784ed5a2020f12626", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "e11393b3f462154ebec5d100859bd5d616e958d9f701e4d62af5764ee84f5213"},
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
}

View File

@ -0,0 +1 @@
ExUnit.start()

View File

@ -0,0 +1,8 @@
defmodule Veml6075Test do
use ExUnit.Case
doctest Veml6075
test "greets the world" do
assert Veml6075.hello() == :world
end
end