Home Posts Tags Post Search Tag Search

Post 64

Weather App 11 - Normalization/Supervisor Tree

Published on: 2025-08-25 Tags: elixir, Blog, Side Project, Libraries, Nerves, Weather App, Poncho
        Include Dependencies for Sensors
            Okay so we are not starting to normalize the data from the sensors. We will do this with the file /sensor_hub/sensor.ex.

            sensor.ex
                defstruct [:name, :fields, :read, :convert]

                def new(name) do
                    %__MODULE__{
                    read: read_fn(name),
                    convert: convert_fn(name),
                    fields: fields(name),
                    name: name
                    }
                end

                #then we need fields(), read(), convert()
                def fields(SPG40), do: [:voc_index]
                def read(SPG40), do: fn -> Sgp40.get_measurement() end
                def convert_fn(SGP40) do
                    fn reading ->
                    Map.take(reading, [:voc_index])
                    end
                end

            With these for each sensor we are now passing the GenServer by name to the new that will start to take measurements from each sensor and turn the general values into something that we can use for each sensor.

            Here are some lines to put into iex so you can test everything:
                alias SensorHub.Sensor

                Bme280.start_link()
                Sgp40.start_link()
                TSL25911FN.start_link()
                LTR390_UV.start_link()

                bme = Sensor.new(Bme280)
                sgp = Sensor.new(Sgp40)
                tsl = Sensor.new(TSL25911FN)
                ltr = Sensor.new(LTR390_UV)

                Sensor.measure(bme)
                Sensor.measure(sgp)
                Sensor.measure(tsl)
                Sensor.measure(ltr)

            To go over a few things the code sinpets above are set to work when the readings give you the exact keys in the map as the fields. I had to do some work to make what I have work as most all the readings will return something like %{last_reading: last_reading} with last_reading having some reference to the values that it will have in it.

        Setting up the Supervisor tree
            We now need to be sure that not only is there a supervisor tree setup to make sure that you have a way to make sure that the GenSevers will restart and all start in the correct order we also need to be sure that we auto start the sensors when we power the Raspberry Pi

            def children(_target) do
                # This function returns the child processes to be supervised
                # Here you can define the children for your application
                [
                {Bme280, []},
                {Sgp40, []},
                {TSL25911FN, []},
                {LTR390_UV, []}
                ]
            end

            and

            @impl true
            def start(_type, _args) do
                # See https://hexdocs.pm/elixir/Supervisor.html
                # for other strategies and supported options

                children =
                children(target())

                opts = [strategy: :one_for_one, name: SensorHub.Supervisor]
                Supervisor.start_link(children, opts)
            end

            Now we can create and upload the firmware and it will start automatically and also we can test the sensors with
            
                Supervisor.which_children(SensorHub.Supervisor)

                Bme280 |> Sensor.new() |> Sensor.measure()

        This is everything that we wanted to deal with up to this point. Next we can deal with some offset for the temp and VOC and then PUBLISHING THE SENSOR DATA.