Mochis NoticiasTecnologíaControl del módulo HVAC automotriz mediante Android
Mochis NoticiasTecnologíaControl del módulo HVAC automotriz mediante Android
Tecnología

Control del módulo HVAC automotriz mediante Android

En el diseño de automóviles moderno, el control de varios componentes de un vehículo a través de un dispositivo móvil se ha convertido en una tendencia importante, lo que mejora la experiencia y la comodidad del usuario. Uno de esos componentes es el sistema HVAC (calefacción, ventilación y aire acondicionado), que desempeña un papel crucial para garantizar la comodidad de los pasajeros. En este artículo, exploraremos cómo controlar el módulo HVAC en un automóvil usando un dispositivo Android, aprovechando el poder del protocolo SOME/IP.

Entendemos HVAC

HVAC significa Calefacción, Ventilación y Aire Acondicionado. En el ámbito de la ingeniería automovilística, el sistema HVAC regula la temperatura, la humedad y la calidad del aire en el habitáculo del vehículo. Incluye componentes como calentadores, aires acondicionados, ventiladores y filtros de aire. El control del sistema HVAC contribuye eficientemente al confort y seguridad de los pasajeros durante el viaje.

Introducción a XI/IP

En el paradigma SOME/IP, la comunicación se estructura en torno a servicios, que encapsulan funcionalidades específicas o intercambios de datos. Hay dos roles principales dentro del modelo orientado a servicios:

Proveedor: El proveedor es responsable de ofrecer servicios a otras ECU dentro de la red. En el contexto automotriz, la ECU de un proveedor puede controlar actuadores físicos, leer datos de sensores o realizar otras tareas relacionadas con el funcionamiento del vehículo. Por ejemplo, en nuestro caso, el proveedor sería una aplicación que se ejecuta en un controlador de dominio en el vehículo.

El proveedor ofrece servicios exponiendo interfaces que definen los métodos o estructuras de datos disponibles para la interacción. Estas interfaces pueden incluir operaciones para controlar actuadores (por ejemplo, configuraciones de HVAC) o métodos para leer datos de sensores (por ejemplo, temperatura, humedad).

Consumidor: El consumidor, por otra parte, es una ECU que utiliza servicios proporcionados por otras ECU de la red. Los consumidores pueden suscribirse a servicios específicos ofrecidos por proveedores para recibir actualizaciones o invocar métodos según sea necesario. En el contexto automotriz, un consumidor puede ser responsable de interpretar los datos de los sensores, enviar comandos de control o realizar otras tareas basadas en la información recibida.

Los consumidores se suscriben a los servicios que les interesan y reciben actualizaciones cada vez que hay nuevos datos disponibles. También podrán invocar métodos proporcionados por el proveedor de servicios para iniciar acciones o funcionalidades de control. En nuestro escenario, el consumidor sería una aplicación que se ejecuta en Android VHAL (Vehicle Hardware Abstraction Layer), responsable de interactuar con la red del vehículo y controlar la configuración de HVAC.

Flujo de comunicación XI/IP

El flujo de comunicación en XI/IP sigue un modelo de publicación-suscripción, donde los proveedores publican datos o servicios y los consumidores se suscriben a ellos para recibir actualizaciones o invocar métodos. Este modelo de comunicación asíncrona permite una interacción eficiente y flexible entre las ECU dentro de la red.

diagrama de flujo de comunicación en IPdiagrama de flujo de comunicación en IP

Fuente: https://github.com/COVESA/vsomeip/wiki/vsomeip-in-10-minutos

En nuestro caso, la aplicación que se ejecuta en el controlador de dominio (proveedor) publica datos de sensores como temperatura, humedad y estado de HVAC. Los consumidores suscritos, como la aplicación VHAL en Android, reciben estas actualizaciones y pueden enviar comandos de control al controlador de dominio para ajustar la configuración de HVAC según la entrada del usuario.

Aprovechando VHAL en Android para redes de vehículos

Para comunicarse con la red del vehículo, Android proporciona la capa de abstracción de hardware del vehículo (VHAL). VHAL actúa como puente entre el sistema operativo Android y los sistemas a bordo del vehículo, permitiendo una integración perfecta de los dispositivos Android con las funcionalidades del vehículo. VHAL abstrae las complejidades de los protocolos de redes de vehículos, lo que permite a los desarrolladores centrarse en implementar funciones como el control HVAC sin preocuparse por los detalles de comunicación de bajo nivel.

Diagrama de arquitectura HVACDiagrama de arquitectura HVAC

Fuente: https://source.android.com/docs/automotive/vhal/previous/properties

Implementar el consumidor SOMEIP en VHAL

Para integrar un consumidor SOMEIP en VHAL en Android 14, usaremos la biblioteca vsomeip. A continuación se detallan los pasos necesarios para implementar esta solución:

Clonación del repositorio vsomeip

Vaya al directorio principal de su proyecto de Android y cree un nuevo directorio llamado external/sdv:

mkdir -p external/sdv
cd external/sdv
git clone https://android.googlesource.com/platform/external/sdv/vsomeip

Implementar el consumidor SOMEIP en VHAL

Jul hardware/interfaces/coches/vehículo/2.0/default directorio, puede encontrar el código de la aplicación VHAL. Jul ServicioVehículo.cpp archivo, encontrará la implementación VHAL predeterminada.

int main(int /* argc */, char* /* argv */ []) {
    auto store = std::make_unique<VehiclePropertyStore>();
    auto connector = std::make_unique<DefaultVehicleConnector>();
    auto hal = std::make_unique<DefaultVehicleHal>(store.get(), connector.get());
    auto service = android::sp<VehicleHalManager>::make(hal.get());
    connector->setValuePool(hal->getValuePool());
    android::hardware::configureRpcThreadpool(4, true /* callerWillJoin */);
    ALOGI("Registering as service...");
    android::status_t status = service->registerAsService();
    if (status != android::OK) {
        ALOGE("Unable to register vehicle service (%d)", status);
        return 1;
    }
    ALOGI("Ready");
    android::hardware::joinRpcThreadpool();
    return 0;
}

La implementación VHAL predeterminada se proporciona en PredeterminadoVehículoHal que queremos cambiar en ella ServicioVehículo.cpp.

De:

auto hal = std::make_unique<DefaultVehicleHal>(store.get(), connector.get());

A:

auto hal = std::make_unique<VendorVehicleHal>(store.get(), connector.get());

Para nuestra implementación, crearemos una clase llamada ProveedorVehículoHal y heredar de la PredeterminadoVehículoHal clase. Superaremos el conjunto y conseguiremos funciones.

class VendorVehicleHal : public DefaultVehicleHal {
public:
    VendorVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client);
 
    VehiclePropValuePtr get(const VehiclePropValue& requestedPropValue,
                            StatusCode* outStatus) override;
    StatusCode set(const VehiclePropValue& propValue) override;
 };

La función get se invoca cuando el sistema Android solicita información de VHAL y se configura cuando quiere configurarla. Los datos se transmiten en un objeto VehiclePropValue definido en hardware/interfaces/automotive/vehicle/2.0/types.hal.

Contiene una variable, prop, que es nuestro identificador de propiedad. La lista de todas las propiedades se puede encontrar en el archivo tipos.hal.

Solo filtraremos los valores de interés y redirigiremos el resto a la implementación predeterminada.

StatusCode VendorVehicleHal::set(const VehiclePropValue& propValue) {
    ALOGD("VendorVehicleHal::set  propId: 0x%x areaID: 0x%x", propValue.prop, propValue.areaId);
 
    switch(propValue.prop)
    {
        case (int)VehicleProperty::HVAC_FAN_SPEED :
        break;
 
        case (int)VehicleProperty::HVAC_FAN_DIRECTION : 
        break;
 
        case (int)VehicleProperty::HVAC_TEMPERATURE_CURRENT :
        break;
 
        case (int)VehicleProperty::HVAC_TEMPERATURE_SET:
        break;
 
        case (int)VehicleProperty::HVAC_DEFROSTER :
        break;
    
        case (int)VehicleProperty::HVAC_AC_ON :
        break;
        
        case (int)VehicleProperty::HVAC_MAX_AC_ON :
        break;
 
        case (int)VehicleProperty::HVAC_MAX_DEFROST_ON :
        break;
 
        case (int)VehicleProperty::EVS_SERVICE_REQUEST :
        break;
 
        case (int)VehicleProperty::HVAC_TEMPERATURE_DISPLAY_UNITS  :
        break;
    }
 
    return DefaultVehicleHal::set(propValue);
}

Ahora necesitamos crear un consumidor de servicios XI/IP. Si no está familiarizado con el protocolo SOME/IP o la biblioteca vsomeip, le recomiendo leer la guía «vsomeip en 10 minutos».

Proporciona una descripción paso a paso de cómo crear un proveedor y un consumidor para XI/IP.

En nuestro ejemplo, crearemos una clase llamada ZoneHVACService y definiremos ALGUNOS ID/IP del servicio, instancia, método y evento:

#define ZONE_HVAC_SERVICE_ID       				0x4002
#define ZONE_HVAC_INSTANCE_ID      				0x0001
 
#define ZONE_HVAC_SET_TEMPERATURE_ID    		0x1011
#define ZONE_HVAC_SET_FANSPEED_ID    			0x1012
#define ZONE_HVAC_SET_AIR_DISTRIBUTION_ID    	0x1013
 
#define ZONE_HVAC_TEMPERATURE_EVENT_ID         	0x2011
#define ZONE_HVAC_FANSPEED_EVENT_ID    			0x2012
#define ZONE_HVAC_AIR_DISTRIBUTION_EVENT_ID    	0x2013
 
#define ZONE_HVAC_EVENT_GROUP_ID         		0x3011
 
class ZoneHVACService {
public:
    ZoneHVACService(bool _use_tcp) :
            app_(vsomeip::runtime::get()->create_application(vsomeipAppName)), use_tcp_(
            _use_tcp) {
    }
 
    bool init() {
        if (!app_->init()) {
            LOG(ERROR) << "[SOMEIP] " << __func__ << "Couldn't initialize application";
            return false;
        } 
 
        app_->register_state_handler(
                std::bind(&ZoneHVACService::on_state, this,
                          std::placeholders::_1));
  
        app_->register_message_handler(
                ZONE_HVAC_SERVICE_ID, ZONE_HVAC_INSTANCE_ID, vsomeip::ANY_METHOD,
                std::bind(&ZoneHVACService::on_message, this,
                          std::placeholders::_1));
 
 
        app_->register_availability_handler(ZONE_HVAC_SERVICE_ID, ZONE_HVAC_INSTANCE_ID,
                                            std::bind(&ZoneHVACService::on_availability,
                                                      this,
                                                      std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
 
        std::set<vsomeip::eventgroup_t> its_groups;
        its_groups.insert(ZONE_HVAC_EVENT_GROUP_ID);
        app_->request_event(
                ZONE_HVAC_SERVICE_ID,
                ZONE_HVAC_INSTANCE_ID,
                ZONE_HVAC_TEMPERATURE_EVENT_ID,
                its_groups,
                vsomeip::event_type_e::ET_FIELD);
        app_->request_event(
                ZONE_HVAC_SERVICE_ID,
                ZONE_HVAC_INSTANCE_ID,
                ZONE_HVAC_FANSPEED_EVENT_ID,
                its_groups,
                vsomeip::event_type_e::ET_FIELD);
        app_->request_event(
                ZONE_HVAC_SERVICE_ID,
                ZONE_HVAC_INSTANCE_ID,
                ZONE_HVAC_AIR_DISTRIBUTION_EVENT_ID,
                its_groups,
                vsomeip::event_type_e::ET_FIELD);
        app_->subscribe(ZONE_HVAC_SERVICE_ID, ZONE_HVAC_INSTANCE_ID, ZONE_HVAC_EVENT_GROUP_ID);
 
        return true;
    }
 
    void send_temp(std::string temp)
    {
        LOG(INFO) << "[SOMEIP] " << __func__ <<  " temp: " << temp;
        std::shared_ptr< vsomeip::message > request;
        request = vsomeip::runtime::get()->create_request();
        request->set_service(ZONE_HVAC_SERVICE_ID);
        request->set_instance(ZONE_HVAC_INSTANCE_ID);
        request->set_method(ZONE_HVAC_SET_TEMPERATURE_ID);
 
        std::shared_ptr< vsomeip::payload > its_payload = vsomeip::runtime::get()->create_payload();
        its_payload->set_data((const vsomeip_v3::byte_t *)temp.data(), temp.size());
        request->set_payload(its_payload);
        app_->send(request);
    }
 
    void send_fanspeed(uint8_t speed)
    {
        LOG(INFO) << "[SOMEIP] " << __func__ <<  " speed: " << (int)speed;
        std::shared_ptr< vsomeip::message > request;
        request = vsomeip::runtime::get()->create_request();
        request->set_service(ZONE_HVAC_SERVICE_ID);
        request->set_instance(ZONE_HVAC_INSTANCE_ID);
        request->set_method(ZONE_HVAC_SET_FANSPEED_ID);
 
        std::shared_ptr< vsomeip::payload > its_payload = vsomeip::runtime::get()->create_payload();
        its_payload->set_data(&speed, 1U);
        request->set_payload(its_payload);
        app_->send(request);
    }
  
    void start() {
        app_->start();
    }
 
    void on_state(vsomeip::state_type_e _state) {
        if (_state == vsomeip::state_type_e::ST_REGISTERED) {
            app_->request_service(ZONE_HVAC_SERVICE_ID, ZONE_HVAC_INSTANCE_ID);
        }
    }
 
    void on_availability(vsomeip::service_t _service, vsomeip::instance_t _instance, bool _is_available) {
        LOG(INFO) << "[SOMEIP] " << __func__ <<  "Service ["
                  << std::setw(4) << std::setfill('0') << std::hex << _service << "." << _instance
                  << "] is "
                  << (_is_available ? "available." : "NOT available.");
    }
 
    void on_temperature_message(const std::shared_ptr<vsomeip::message> & message)
    {
        auto payload = message->get_payload();
        temperature_.resize(payload->get_length());
        temperature_.assign((char*)payload->get_data(), payload->get_length());
        LOG(INFO) << "[SOMEIP] " << __func__ <<  " temp: " << temperature_;
 
        if(tempChanged_)
        {
            tempChanged_(temperature_);
        }
    }
 
    void on_fanspeed_message(const std::shared_ptr<vsomeip::message> & message)
    {
        auto payload = message->get_payload();
        fan_speed_ = *payload->get_data();
        LOG(INFO) << "[SOMEIP] " << __func__ <<  " speed: " << (int)fan_speed_;
 
        if(fanspeedChanged_)
        {
            fanspeedChanged_(fan_speed_);
        }
    }
 
    void on_message(const std::shared_ptr<vsomeip::message> & message) {
        if(message->get_method() == ZONE_HVAC_TEMPERATURE_EVENT_ID)
        {
            LOG(INFO) << "[SOMEIP] " << __func__ << "TEMPERATURE_EVENT_ID received";
            on_temperature_message(message);
        }
       else  if(message->get_method() == ZONE_HVAC_FANSPEED_EVENT_ID)
        {
            LOG(INFO) << "[SOMEIP] " << __func__ << "ZONE_HVAC_FANSPEED_EVENT_ID received";
            on_fanspeed_message(message);
        }
    }
 
 
    std::function<void(std::string temp)> tempChanged_;
    std::function<void(uint8_t)> fanspeedChanged_;
 
private:
    std::shared_ptr< vsomeip::application > app_;
    bool use_tcp_;
 
    std::string temperature_;
    uint8_t fan_speed_;
    uint8_t air_distribution_t;
};

En nuestro ejemplo, conectaremos ZoneHVACService y VendorVehicleHal mediante devoluciones de llamada.

hal->fandirectionChanged_ = [&](uint8_t direction) {
 ALOGI("HAL fandirectionChanged_ callback direction: %u", direction);
 hvacService->send_fandirection(direction);
};
hal->fanspeedChanged_ = [&](uint8_t speed) {
 ALOGI("HAL fanspeedChanged_ callback speed: %u", speed);
 hvacService->send_fanspeed(speed);
};

Lo último que nos queda por hacer es crear una configuración para la biblioteca vsomeip. Lo mejor es utilizar un archivo de muestra de la biblioteca: https://github.com/COVESA/vsomeip/blob/master/config/vsomeip-local.json

En este archivo, debe cambiar la dirección:

«unidifusión»: «10.0.2.15»,

en la dirección de nuestro dispositivo Android.

Además, debes configurar:

«enrutamiento»: «muestra de servicio»,

para el nombre de nuestra aplicación.

La pila vsomeip lee la dirección de la aplicación y la ruta al archivo de configuración desde la variable de entorno. La forma más sencilla de hacer esto en Android es detenerse antes de crear el objeto ZoneHVACService.

setenv("VSOMEIP_CONFIGURATION","/vendor/etc/vsomeip-local-hvac.json",1);
setenv("VSOMEIP_APPLICATION_NAME," "hvac-service",1);

Eso es todo. Ahora tenemos que reemplazar proveedor/bin/hw/android.hardware.automotive.vehicle@2.0-default-service con nuestra nueva compilación y reinicie Android.

Si todo se configuró correctamente, deberíamos ver registros como estos y el proveedor debería recibir nuestras solicitudes.


04-25 06:52:12.989  3981  3981 I automotive.vehicle@2.0-default-service: Starting automotive.vehicle@2.0-default-service ...
04-25 06:52:13.005  3981  3981 I automotive.vehicle@2.0-default-service: Registering as service...
04-25 06:52:13.077  3981  3981 I automotive.vehicle@2.0-default-service: Ready
04-25 06:52:13.081  3981  4011 I automotive.vehicle@2.0-default-service: Starting UDP receiver
04-25 06:52:13.081  3981  4011 I automotive.vehicle@2.0-default-service: Socket created
04-25 06:52:13.082  3981  4010 I automotive.vehicle@2.0-default-service: HTTPServer starting
04-25 06:52:13.082  3981  4010 I automotive.vehicle@2.0-default-service: HTTPServer listen
04-25 06:52:13.091  3981  4012 I automotive.vehicle@2.0-default-service: Initializing SomeIP service ...
04-25 06:52:13.091  3981  4012 I automotive.vehicle@2.0-default-service: [SOMEIP] initInitialize app
04-25 06:52:13.209  3981  4012 I automotive.vehicle@2.0-default-service: [SOMEIP] initApp initialized
04-25 06:52:13.209  3981  4012 I automotive.vehicle@2.0-default-service: [SOMEIP] initClient settings [protocol=UDP]
04-25 06:52:13.210  3981  4012 I automotive.vehicle@2.0-default-service: [SOMEIP] Initialized SomeIP service result:1
04-25 06:52:13.214  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_availabilityService [4002.1] is NOT available.
04-25 06:54:35.654  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_availabilityService [4002.1] is available.
04-25 06:54:35.774  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_message Message received: [4002.0001.2012] to Client/Session [0000/0002]
04-25 06:54:35.774  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_messageZONE_HVAC_FANSPEED_EVENT_ID received
04-25 06:54:35.774  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_fanspeed_message speed: 1
04-25 06:54:35.775  3981  4028 I automotive.vehicle@2.0-default-service: SOMEIP fanspeedChanged_ speed: 1
04-25 06:54:36.602  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_message Message received: [4002.0001.2012] to Client/Session [0000/0003]
04-25 06:54:36.602  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_messageZONE_HVAC_FANSPEED_EVENT_ID received
04-25 06:54:36.603  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_fanspeed_message speed: 2
04-25 06:54:36.603  3981  4028 I automotive.vehicle@2.0-default-service: SOMEIP fanspeedChanged_ speed: 2
04-25 06:54:37.605  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_message Message received: [4002.0001.2012] to Client/Session [0000/0004]
04-25 06:54:37.606  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_messageZONE_HVAC_FANSPEED_EVENT_ID received
04-25 06:54:37.606  3981  4028 I automotive.vehicle@2.0-default-service: [SOMEIP] on_fanspeed_message speed: 3
04-25 06:54:37.606  3981  4028 I automotive.vehicle@2.0-default-service: SOMEIP fanspeedChanged_ speed: 3

Resumen

En conclusión, la integración de dispositivos Android con Vehicle Hardware Abstraction Layer (VHAL) para el control de sistemas HVAC abre un nuevo campo de posibilidades para la tecnología automotriz. Al aprovechar el poder del protocolo de comunicación SOME/IP y la biblioteca vsomeip, los desarrolladores pueden crear soluciones sólidas para administrar las funcionalidades HVAC de los vehículos.

Siguiendo los pasos descritos en este artículo, los desarrolladores pueden crear implementaciones VHAL personalizadas adaptadas a sus necesidades específicas. Desde la definición de interfaces de servicio hasta el manejo de devoluciones de llamadas de comunicación, cada aspecto del proceso de integración se ha explicado cuidadosamente para facilitar un desarrollo fluido.

A medida que la tecnología automotriz continúa evolucionando, la convergencia de dispositivos Android y sistemas de vehículos representa un paso significativo en el camino hacia vehículos más inteligentes y conectados. La integración de funcionalidades de control de HVAC a través de VHAL y SOME/IP no sólo demuestra el potencial de la tecnología automotriz moderna sino que también allana el camino para futuras innovaciones en este campo.

Source link

Hi, I’m Corina Guzman

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *