-
-
Save mitchellhenke/5ad58a86e7e56c5c056d588f54175449 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule HomeSensor.BME280 do | |
use GenServer | |
use Bitwise | |
require Logger | |
alias Circuits.I2C | |
@address 0x77 | |
@chip_id 0x60 | |
@chip_id_register 0xD0 | |
@dig_t1_register 0x88 | |
@dig_h1_register 0xA1 | |
@dig_h2_register 0xE1 | |
# @dig_h3_register 0xE3 | |
# @dig_h4_register 0xE4 | |
# @dig_h5_register 0xE5 | |
# @dig_h6_register 0xE7 | |
@soft_reset_register 0xE0 | |
@ctrl_hum_register 0xF2 | |
@status_register 0xF3 | |
@ctrl_meas_register 0xF4 | |
@config_register 0xF5 | |
@pressure_data_register 0xF7 | |
# @temp_data_register 0xFA | |
# @humid_data_register 0xFD | |
@iir_filter_disable 0x00 | |
# @overscan_disable 0x00 | |
@overscan_x1 0x01 | |
@mode_sleep 0x00 | |
@mode_force 0x01 | |
# @mode_normal 0x03 | |
# 1000ms | |
# @standby_tc_1000 0x05 | |
defstruct i2c_ref: nil, | |
mode: @mode_sleep, | |
iir: @iir_filter_disable, | |
overscan: @overscan_x1, | |
temp_calib: {nil, nil, nil}, | |
pressure_calib: {nil, nil, nil, nil, nil, nil, nil, nil, nil}, | |
humidity_calib: {nil, nil, nil, nil, nil, nil} | |
def start_link(_ \\ nil) do | |
GenServer.start_link(__MODULE__, nil) | |
end | |
def read_temperature(pid) do | |
GenServer.call(pid, :read_temperature) | |
end | |
@impl true | |
def init(_state) do | |
{:ok, ref} = I2C.open("i2c-1") | |
Process.send_after(self(), :initialize, 0) | |
Process.send_after(self(), :read_temperature, 0) | |
{:ok, %__MODULE__{i2c_ref: ref}} | |
end | |
@impl true | |
def handle_call({:write_read, register, bytes}, _from, %{i2c_ref: ref} = state) do | |
response = I2C.write_read!(ref, @address, <<register>>, bytes) | |
{:reply, response, state} | |
end | |
@impl true | |
def handle_call(:get, _from, state) do | |
{:reply, state, state} | |
end | |
@impl true | |
def handle_call(:read_temperature, _from, %{i2c_ref: ref} = state) do | |
:ok = I2C.write(ref, @address, <<@ctrl_hum_register, @overscan_x1>>) | |
ctrl_meas = (@overscan_x1 <<< 5) + (@overscan_x1 <<< 2) + @mode_force | |
:ok = I2C.write(ref, @address, <<@ctrl_meas_register, ctrl_meas>>) | |
wait_for_status(ref) | |
{humidity, pressure_hpa, temperature_f} = | |
read(ref, {state.temp_calib, state.pressure_calib, state.humidity_calib}) | |
{:reply, {humidity, pressure_hpa, temperature_f}, state} | |
end | |
@impl true | |
def handle_info(:read_temperature, %{i2c_ref: ref} = state) do | |
:ok = I2C.write(ref, @address, <<@ctrl_hum_register, @overscan_x1>>) | |
ctrl_meas = (@overscan_x1 <<< 5) + (@overscan_x1 <<< 2) + @mode_force | |
:ok = I2C.write(ref, @address, <<@ctrl_meas_register, ctrl_meas>>) | |
wait_for_status(ref) | |
{humidity, pressure_hpa, temperature_f} = | |
read(ref, {state.temp_calib, state.pressure_calib, state.humidity_calib}) | |
data = | |
"weather,source=pi,location=downstairs temperature=#{temperature_f},humidity=#{humidity},pressure=#{ | |
pressure_hpa | |
}" | |
auth = "Basic #{Base.encode64("username:password")}" | |
r = | |
HTTPoison.post("https://influxdb_server.domain.com/write?db=db_name", data, [ | |
{"Authorization", auth} | |
]) | |
Process.send_after(self(), :read_temperature, 60_000) | |
{:noreply, state} | |
end | |
@impl true | |
def handle_info(:initialize, %{i2c_ref: ref} = state) do | |
with {:ok, <<@chip_id>>} <- I2C.write_read(ref, @address, <<@chip_id_register>>, 1), | |
:ok <- I2C.write(ref, @address, <<@soft_reset_register, 0xB6>>), | |
:ok <- :timer.sleep(4), | |
{:ok, | |
<<t1::little-unsigned-integer-size(16), t2::little-signed-integer-size(16), | |
t3::little-signed-integer-size(16), p1::little-unsigned-integer-size(16), | |
p2::little-signed-integer-size(16), p3::little-signed-integer-size(16), | |
p4::little-signed-integer-size(16), p5::little-signed-integer-size(16), | |
p6::little-signed-integer-size(16), p7::little-signed-integer-size(16), | |
p8::little-signed-integer-size(16), | |
p9::little-signed-integer-size(16)>>} <- | |
I2C.write_read(ref, @address, <<@dig_t1_register>>, 24), | |
{:ok, <<h1::little-unsigned-integer-size(8)>>} <- | |
I2C.write_read(ref, @address, <<@dig_h1_register>>, 1), | |
{:ok, | |
<<h2::little-signed-integer-size(16), h3::little-unsigned-integer-size(8), | |
h4::little-signed-integer-size(8), h5::little-unsigned-integer-size(8), | |
h6::little-signed-integer-size(8), | |
h7::little-signed-integer-size(8)>>} <- | |
I2C.write_read(ref, @address, <<@dig_h2_register>>, 7), | |
# WRITE_CONFIG | |
# Forced Mode and IIR disabled | |
:ok <- I2C.write(ref, @address, <<@ctrl_hum_register, @overscan_x1>>), | |
ctrl_meas <- (@overscan_x1 <<< 5) + (@overscan_x1 <<< 2) + @mode_force, | |
:ok <- I2C.write(ref, @address, <<@ctrl_meas_register, ctrl_meas>>), | |
:ok <- I2C.write(ref, @address, <<@config_register, 0x00>>) do | |
h2 = h2 / 1 | |
h3 = h3 / 1 | |
h4 = h4 <<< 4 ||| (h5 &&& 0xF) | |
h5 = h6 <<< 4 ||| h5 >>> 4 | |
h6 = h7 / 1 | |
state = %{ | |
state | |
| temp_calib: {t1, t2, t3}, | |
pressure_calib: {p1, p2, p3, p4, p5, p6, p7, p8, p9}, | |
humidity_calib: {h1, h2, h3, h4, h5, h6} | |
} | |
{:noreply, state} | |
else | |
e -> | |
Logger.error("Failed to initialize - #{inspect(e)}") | |
{:noreply, state} | |
end | |
end | |
def handle_info({:ssl_closed, {:sslsocket, _, _}}, state) do | |
{:noreply, state} | |
end | |
def read(ref, {temp_calib, pressure_calib, humidity_calib}) do | |
<<p1::little-unsigned-integer-size(8), p2::little-unsigned-integer-size(8), | |
p3::little-unsigned-integer-size(8), t1::little-unsigned-integer-size(8), | |
t2::little-unsigned-integer-size(8), t3::little-unsigned-integer-size(8), | |
h1::little-unsigned-integer-size(8), | |
h2::little-unsigned-integer-size(8)>> = | |
I2C.write_read!(ref, @address, <<@pressure_data_register>>, 8) | |
{temp_calib1, temp_calib2, temp_calib3} = temp_calib | |
raw_temperature = ((t1 * 256.0 + t2) * 256.0 + t3) / 16 | |
var1 = (raw_temperature / 16384.0 - temp_calib1 / 1024.0) * temp_calib2 | |
var2 = | |
(raw_temperature / 131_072.0 - temp_calib1 / 8192.0) * | |
(raw_temperature / 131_072.0 - temp_calib1 / 8192.0) * temp_calib3 | |
t_fine = var1 + var2 | |
temperature_c = t_fine / 5120.0 | |
temperature_f = Float.round(temperature_c * 9 / 5 + 32.0, 2) | |
{p_calib0, p_calib1, p_calib2, p_calib3, p_calib4, p_calib5, p_calib6, p_calib7, p_calib8} = | |
pressure_calib | |
raw_pressure = ((p1 * 256.0 + p2) * 256.0 + p3) / 16 | |
var1 = t_fine / 2.0 - 64000.0 | |
var2 = var1 * var1 * p_calib5 / 32768.0 | |
var2 = var2 + var1 * p_calib4 * 2.0 | |
var2 = var2 / 4.0 + p_calib3 * 65536.0 | |
var3 = p_calib2 * var1 * var1 / 524_288.0 | |
var1 = (var3 + p_calib1 * var1) / 524_288.0 | |
var1 = (1.0 + var1 / 32768.0) * p_calib0 | |
pressure = 1_048_576.0 - raw_pressure | |
pressure = (pressure - var2 / 4096.0) * 6250.0 / var1 | |
var1 = p_calib8 * pressure * pressure / 2_147_483_648.0 | |
var2 = pressure * p_calib7 / 32768.0 | |
pressure = pressure + (var1 + var2 + p_calib6) / 16.0 | |
# hectoPascals | |
pressure_hpa = pressure / 100 | |
# HUMIDITY | |
{h_calib0, h_calib1, h_calib2, h_calib3, h_calib4, h_calib5} = humidity_calib | |
adc = h1 <<< 8 ||| h2 | |
var1 = t_fine - 76800.0 | |
var2 = h_calib3 * 64.0 + h_calib4 / 16384.0 * var1 | |
var3 = adc - var2 | |
var4 = h_calib1 / 65536.0 | |
var5 = 1.0 + h_calib2 / 67_108_864.0 * var1 | |
var6 = 1.0 + h_calib5 / 67_108_864.0 * var1 * var5 | |
var6 = var3 * var4 * (var5 * var6) | |
humidity = var6 * (1.0 - h_calib0 * var6 / 524_288.0) / 100 | |
{humidity, pressure_hpa, temperature_f} | |
end | |
def wait_for_status(ref) do | |
<<status::little-unsigned-integer-size(8)>> = | |
I2C.write_read!(ref, @address, <<@status_register>>, 1) | |
Logger.info("Status is #{inspect(status)}") | |
if (status &&& 0x08) > 0 do | |
Logger.error("Status is not ready, it is: #{inspect(status)}") | |
:timer.sleep(2) | |
wait_for_status(ref) | |
else | |
:ok | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment