Home Posts Post Search Tag Search

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.