Android AAOS 14 – 4 Zone HVAC


05/08/2024

near 11 min of reading

In this article, we will explore the implementation of a four-zone climate control system for vehicles using Android Automotive OS (AAOS) version 14. Multi-zone climate control systems allow individual passengers to adjust the temperature for their specific areas, enhancing comfort and personalizing the in-car experience. We will delve into the architecture, components, and integration steps necessary to create a robust and efficient four-zone HVAC system within the AAOS environment.

Understanding Four-Zone Climate Control

A four-zone climate control system divides the vehicle’s cabin into four distinct areas: the driver, front passenger, left rear passenger, and right rear passenger. Each zone can be independently controlled to set the desired temperature. This system enhances passenger comfort by accommodating individual preferences and ensuring an optimal environment for all occupants.

Modifying SystemUI for Four-Zone HVAC in Android AAOS14

To implement a four-zone HVAC system in Android AAOS14, we first need to modify the SystemUI, which handles the user interface. The application is located in packages/apps/Car/SystemUI. The HVAC panel is defined in the file res/layout/hvac_panel.xml.

Here is an example definition of the HVAC panel with four sliders for temperature control and four buttons for seat heating:

<!--
  ~ Copyright (C) 2022 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->
 
<com.android.systemui.car.hvac.HvacPanelView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/hvac_panel"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="@dimen/hvac_panel_full_expanded_height"
    android:background="@color/hvac_background_color">
    
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/top_guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="@dimen/hvac_panel_top_padding"/>
        
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/bottom_guideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_end="@dimen/hvac_panel_bottom_padding"/>
        
    <!-- HVAC property IDs can be found in VehiclePropertyIds.java, and the area IDs depend on each OEM's VHAL implementation. -->
 
<com.android.systemui.car.hvac.referenceui.BackgroundAdjustingTemperatureControlView
        android:id="@+id/driver_hvac"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/row2_driver_hvac"
        systemui:hvacAreaId="1">
        <include layout="@layout/hvac_temperature_bar_overlay"/>

</com.android.systemui.car.hvac.referenceui.BackgroundAdjustingTemperatureControlView>
    
<com.android.systemui.car.hvac.referenceui.BackgroundAdjustingTemperatureControlView
        android:id="@+id/row2_driver_hvac"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/driver_hvac"
        app:layout_constraintBottom_toBottomOf="parent"
        systemui:hvacAreaId="16">
        <include layout="@layout/hvac_temperature_bar_overlay"/>

</com.android.systemui.car.hvac.referenceui.BackgroundAdjustingTemperatureControlView>
 
    <com.android.systemui.car.hvac.SeatTemperatureLevelButton
        android:id="@+id/seat_heat_level_button_left"
        android:background="@drawable/hvac_panel_button_bg"
        style="@style/HvacButton"
        app:layout_constraintTop_toBottomOf="@+id/top_guideline"
        app:layout_constraintLeft_toRightOf="@+id/driver_hvac"
        app:layout_constraintBottom_toTopOf="@+id/recycle_air_button"
        systemui:hvacAreaId="1"
        systemui:seatTemperatureType="heating"

 systemui:seatTemperatureIconDrawableList="@array/hvac_heated_seat_default_icons"/>
        
    <com.android.systemui.car.hvac.toggle.HvacBooleanToggleButton
        android:id="@+id/recycle_air_button"
        android:layout_width="@dimen/hvac_panel_button_dimen"
        android:layout_height="@dimen/hvac_panel_group_height"
        android:background="@drawable/hvac_panel_button_bg"
        app:layout_constraintTop_toBottomOf="@+id/seat_heat_level_button_left"
        app:layout_constraintLeft_toRightOf="@+id/driver_hvac"
        app:layout_constraintBottom_toTopOf="@+id/row2_seat_heat_level_button_left"
        systemui:hvacAreaId="117"
        systemui:hvacPropertyId="354419976"
        systemui:hvacTurnOffIfAutoOn="true"
        systemui:hvacToggleOnButtonDrawable="@drawable/ic_recycle_air_on"
        systemui:hvacToggleOffButtonDrawable="@drawable/ic_recycle_air_off"/>
 
    <com.android.systemui.car.hvac.SeatTemperatureLevelButton
        android:id="@+id/row2_seat_heat_level_button_left"
        android:background="@drawable/hvac_panel_button_bg"
        style="@style/HvacButton"
        app:layout_constraintTop_toBottomOf="@+id/recycle_air_button"
        app:layout_constraintLeft_toRightOf="@+id/row2_driver_hvac"
        app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
        systemui:hvacAreaId="16"
        systemui:seatTemperatureType="heating"

systemui:seatTemperatureIconDrawableList="@array/hvac_heated_seat_default_icons"/>
 
    <LinearLayout
        android:id="@+id/fan_control"
        android:background="@drawable/hvac_panel_button_bg"
        android:layout_width="@dimen/hvac_fan_speed_bar_width"
        android:layout_height="@dimen/hvac_panel_group_height"
        app:layout_constraintTop_toBottomOf="@+id/top_guideline"
        app:layout_constraintLeft_toRightOf="@+id/seat_heat_level_button_left"
        app:layout_constraintRight_toLeftOf="@+id/seat_heat_level_button_right"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:orientation="vertical">
        <com.android.systemui.car.hvac.referenceui.FanSpeedBar
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="0dp"/>
        <com.android.systemui.car.hvac.referenceui.FanDirectionButtons
            android:layout_weight="1"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
            android:layoutDirection="ltr"/>
    </LinearLayout>
 
    <com.android.systemui.car.hvac.toggle.HvacBooleanToggleButton
        android:id="@+id/ac_master_switch"
        android:background="@drawable/hvac_panel_button_bg"
        android:scaleType="center"
        style="@style/HvacButton"
        app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
        app:layout_constraintLeft_toRightOf="@+id/row2_seat_heat_level_button_left"
        systemui:hvacAreaId="117"
        systemui:hvacPropertyId="354419984"
        systemui:hvacTurnOffIfPowerOff="false"
        systemui:hvacToggleOnButtonDrawable="@drawable/ac_master_switch_on"
        systemui:hvacToggleOffButtonDrawable="@drawable/ac_master_switch_off"/>
 
    <com.android.systemui.car.hvac.toggle.HvacBooleanToggleButton
        android:id="@+id/defroster_button"
        android:background="@drawable/hvac_panel_button_bg"
        style="@style/HvacButton"
        app:layout_constraintLeft_toRightOf="@+id/ac_master_switch"
        app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
        systemui:hvacAreaId="1"
        systemui:hvacPropertyId="320865540"
        systemui:hvacToggleOnButtonDrawable="@drawable/ic_front_defroster_on"
        systemui:hvacToggleOffButtonDrawable="@drawable/ic_front_defroster_off"/>
 
    <com.android.systemui.car.hvac.toggle.HvacBooleanToggleButton
        android:id="@+id/auto_button"
        android:background="@drawable/hvac_panel_button_bg"
        systemui:hvacAreaId="117"
        systemui:hvacPropertyId="354419978"
        android:scaleType="center"
        android:layout_gravity="center"
        android:layout_width="0dp"
        style="@style/HvacButton"
        app:layout_constraintLeft_toRightOf="@+id/defroster_button"
        app:layout_constraintRight_toLeftOf="@+id/rear_defroster_button"
        app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
        systemui:hvacToggleOnButtonDrawable="@drawable/ic_auto_on"
        systemui:hvacToggleOffButtonDrawable="@drawable/ic_auto_off"/>
 
    <com.android.systemui.car.hvac.toggle.HvacBooleanToggleButton
        android:id="@+id/rear_defroster_button"
        android:background="@drawable/hvac_panel_button_bg"
        style="@style/HvacButton"
        app:layout_constraintLeft_toRightOf="@+id/auto_button"
        app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
        systemui:hvacAreaId="2"
        systemui:hvacPropertyId="320865540"
        systemui:hvacToggleOnButtonDrawable="@drawable/ic_rear_defroster_on"
        systemui:hvacToggleOffButtonDrawable="@drawable/ic_rear_defroster_off"/>
        
<com.android.systemui.car.hvac.referenceui.BackgroundAdjustingTemperatureControlView
        android:id="@+id/passenger_hvac"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/row2_passenger_hvac"
        systemui:hvacAreaId="2">
        <include layout="@layout/hvac_temperature_bar_overlay"/>

 </com.android.systemui.car.hvac.referenceui.BackgroundAdjustingTemperatureControlView>
    
 <com.android.systemui.car.hvac.referenceui.BackgroundAdjustingTemperatureControlView
        android:id="@+id/row2_passenger_hvac"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/passenger_hvac"
        app:layout_constraintBottom_toBottomOf="parent"
        systemui:hvacAreaId="32">
        <include layout="@layout/hvac_temperature_bar_overlay"/>

 </com.android.systemui.car.hvac.referenceui.BackgroundAdjustingTemperatureControlView>
    
    <com.android.systemui.car.hvac.SeatTemperatureLevelButton
        android:id="@+id/seat_heat_level_button_right"
        android:background="@drawable/hvac_panel_button_bg"
        style="@style/HvacButton"
        app:layout_constraintTop_toBottomOf="@+id/top_guideline"
        app:layout_constraintRight_toLeftOf="@+id/passenger_hvac"
        app:layout_constraintBottom_toTopOf="@+id/row2_seat_heat_level_button_right"
        systemui:hvacAreaId="2"
        systemui:seatTemperatureType="heating"

systemui:seatTemperatureIconDrawableList="@array/hvac_heated_seat_default_icons"/>
        
    <com.android.systemui.car.hvac.SeatTemperatureLevelButton
        android:id="@+id/row2_seat_heat_level_button_right"
        android:background="@drawable/hvac_panel_button_bg"
        style="@style/HvacButton"
        app:layout_constraintTop_toBottomOf="@+id/seat_heat_level_button_right"
        app:layout_constraintRight_toLeftOf="@+id/row2_passenger_hvac"
        app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
        systemui:hvacAreaId="32"
        systemui:seatTemperatureType="heating"

systemui:seatTemperatureIconDrawableList="@array/hvac_heated_seat_default_icons"/>
</com.android.systemui.car.hvac.HvacPanelView>

The main changes are:

  • Adding BackgroundAdjustingTemperatureControlView for each zone and changing their systemui:hvacAreaId to match the values from VehicleAreaSeat::ROW_1_LEFT, VehicleAreaSeat::ROW_2_LEFT, VehicleAreaSeat::ROW_1_RIGHT, and VehicleAreaSeat::ROW_2_RIGHT.
  • Adding SeatTemperatureLevelButton for each zone.

The layout needs to be arranged properly to match the desired design. Information on how to describe the layout in XML can be found at Android Developers – Layout resource.

The presented layout also requires changing the constant values in the res/values/dimens.xml file. Below is the diff with my changes:

diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 11649d4..3f96413 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -73,7 +73,7 @@
     <dimen name="car_primary_icon_size">@*android:dimen/car_primary_icon_size</dimen>

     <dimen name="hvac_container_padding">16dp</dimen>
-    <dimen name="hvac_temperature_bar_margin">32dp</dimen>
+    <dimen name="hvac_temperature_bar_margin">16dp</dimen>
     <dimen name="hvac_temperature_text_size">56sp</dimen>
     <dimen name="hvac_temperature_text_padding">8dp</dimen>
     <dimen name="hvac_temperature_button_size">76dp</dimen>
@@ -295,9 +295,9 @@
     <dimen name="hvac_panel_row_animation_height_shift">0dp</dimen>

     <dimen name="temperature_bar_collapsed_width">96dp</dimen>
-    <dimen name="temperature_bar_expanded_width">96dp</dimen>
+    <dimen name="temperature_bar_expanded_width">128dp</dimen>
     <dimen name="temperature_bar_collapsed_height">96dp</dimen>
-    <dimen name="temperature_bar_expanded_height">356dp</dimen>
+    <dimen name="temperature_bar_expanded_height">200dp</dimen>
     <dimen name="temperature_bar_icon_margin">20dp</dimen>
     <dimen name="temperature_bar_close_icon_dimen">96dp</dimen>

VHAL Configuration

The next step is to add additional zones to the VHAL configuration. The configuration file is located at hardware/interfaces/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h.

In my example, I modified HVAC_SEAT_TEMPERATURE and HVAC_TEMPERATURE_SET:

{.config = {.prop = toInt(VehicleProperty::HVAC_SEAT_TEMPERATURE),
            .access = VehiclePropertyAccess::READ_WRITE,
            .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
            .areaConfigs = {VehicleAreaConfig{
                                    .areaId = SEAT_1_LEFT,
                                    .minInt32Value = -3,
                                    .maxInt32Value = 3,
                            },
                            VehicleAreaConfig{
                                    .areaId = SEAT_1_RIGHT,
                                    .minInt32Value = -3,
                                    .maxInt32Value = 3,
                            },
                            VehicleAreaConfig{
                                    .areaId = SEAT_2_LEFT,
                                    .minInt32Value = -3,
                                    .maxInt32Value = 3,
                            },
                            VehicleAreaConfig{
                                    .areaId = SEAT_2_RIGHT,
                                    .minInt32Value = -3,
                                    .maxInt32Value = 3,
                            },
                            }},
     .initialValue = {.int32Values = {0}}},  // +ve values for heating and -ve for cooling

{.config = {.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET),
            .access = VehiclePropertyAccess::READ_WRITE,
            .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
            .configArray = {160, 280, 5, 605, 825, 10},
            .areaConfigs = {VehicleAreaConfig{
                                    .areaId = (int)(VehicleAreaSeat::ROW_1_LEFT),
                                    .minFloatValue = 16,
                                    .maxFloatValue = 32,
                            },
                            VehicleAreaConfig{
                                    .areaId = (int)(VehicleAreaSeat::ROW_1_RIGHT),
                                    .minFloatValue = 16,
                                    .maxFloatValue = 32,
                            },
                            VehicleAreaConfig{
                                    .areaId = (int)(VehicleAreaSeat::ROW_2_LEFT),
                                    .minFloatValue = 16,
                                    .maxFloatValue = 32,
                            },
                            VehicleAreaConfig{
                                    .areaId = (int)(VehicleAreaSeat::ROW_2_RIGHT),
                                    .minFloatValue = 16,
                                    .maxFloatValue = 32,
                            }
                    }},
     .initialAreaValues = {{(int)(VehicleAreaSeat::ROW_1_LEFT), {.floatValues = {16}}},
                           {(int)(VehicleAreaSeat::ROW_1_RIGHT), {.floatValues = {17}}},
                           {(int)(VehicleAreaSeat::ROW_2_LEFT), {.floatValues = {16}}},
                           {(int)(VehicleAreaSeat::ROW_2_RIGHT), {.floatValues = {19}}},
                        }},

This configuration modifies the HVAC seat temperature and temperature set properties to include all four zones: front left, front right, rear left, and rear right. The areaId for each zone is specified accordingly. The minInt32Value and maxInt32Value for seat temperatures are set to -3 and 3, respectively, while the temperature range is set between 16 and 32 degrees Celsius.

After modifying the VHAL configuration, the new values will be transmitted to the VendorVehicleHal. This ensures that the HVAC settings are accurately reflected and controlled within the system. For detailed information on how to use these configurations and further transmit this data over the network, refer to our articles: “Controlling HVAC Module in Cars Using Android: A Dive into SOME/IP Integration” and “Integrating HVAC Control in Android with DDS”. These resources provide comprehensive guidance on leveraging network protocols like SOME/IP and DDS for effective HVAC module control in automotive systems.

Building the Application

Building the SystemUI and VHAL components requires specific commands and steps to ensure they are correctly compiled and deployed.

mmma packages/apps/Car/SystemUI/
mmma hardware/interfaces/automotive/vehicle/2.0/default/

Uploading the Applications

After building the SystemUI and VHAL, you need to upload the compiled applications to the device. Use the following commands:

adb push out/target/product/rpi4/system/system_ext/priv-app/CarSystemUI/CarSystemUI.apk /system/system_ext/priv-app/CarSystemUI/

adb push out/target/product/rpi4/vendor/bin/hw/android.hardware.automotive.vehicle@2.0-default-service /vendor/bin/hw

Conclusion

In this guide, we covered the steps necessary to modify the HVAC configurations by updating the XML layout and VHAL configuration files. We also detailed the process of building and deploying the SystemUI and VHAL components to your target device.

By following these steps, you ensure that your system reflects the desired changes and operates as intended.



Is it insightful?
Share the article!



Check related articles


Read our blog and stay informed about the industry's latest trends and solutions.


see all articles



Controlling HVAC Module in Cars Using Android: A Dive into SOME/IP Integration


Read the article

Integrating HVAC Control in Android with DDS


Read the article