Return to Home Page

Ignition State Observer

The ignition state observer is the most important one in the whole system, and the reason for the Pico part of the project existing.

The central limitation of the e39 navigation wiring is that the plugs for the navigation module don’t have access to the Switched 12v power. The only way to turn the system on and off is to listen to the IBus ignition key events.

Ignition key data

The Ignition Key event data has been studied before:

Wilhelm Docs Bimmer Board

80 04 BF 11 00 2A   # KL-30  -- position 0 (always hot?)
80 04 BF 11 01 2B   # KL-R   -- position 1 (accessory)
80 04 BF 11 03 29   # KL-15  -- position 2 (on)
80 04 BF 11 07 2D   # KL-50  -- position 3 (start)

Simulated Ignition Key

The Ignition Key events are sent from the GM (General Module) to the bus. I don’t have a GM wired up to my test bench, so I wrote several “SimulatedIgnition” events that do the same things for test purposes.

These events can be sent through the PicoCommsDebugWindow on the e39-rpi HMI.

void IgnitionStateNoVideoObserver::onNewPacket(std::shared_ptr<pico::ibus::data::IbusPacket> iBusPacket) {

        //https://github.com/piersholt/wilhelm-docs/blob/master/ike/11.md
        //http://www.bimmerboard.com/forums/posts/860334
        //80 04 BF 11 00 2A   # KL-30  -- position 0 (always hot?)
        //80 04 BF 11 01 2B   # KL-R   -- position 1 (accessory)
        //80 04 BF 11 03 29   # KL-15  -- position 2 (on)
        //80 04 BF 11 07 2D   # KL-50  -- position 3 (start)


        if (iBusPacket->getSourceDevice() == data::IbusDeviceEnum::IKE && iBusPacket->getDestinationDevice() == 0xBF) {

            if (iBusPacket->getDataLength() >= 2) {
                if (iBusPacket->getRawPacket()[data::IbusPacket::DATA_START + 0] == 0x11) {
                    uint8_t position = iBusPacket->getRawPacket()[data::IbusPacket::DATA_START + 1];

                    if (ignoreFutureRealIgnitionEmissions) {
                        //logger->i(getTag(), fmt::format("Ignoring real ignition status with position {:x}", position));
                    } else {
                        switch (position) {
                            case 0x00:
                                onIgnitionKeyPosition(0);
                                break;
                            case 0x01:
                                onIgnitionKeyPosition(1);
                                break;
                            case 0x03:
                                onIgnitionKeyPosition(2);
                                break;
                            case 0x07:
                                onIgnitionKeyPosition(3);
                                break;
                            default: break;
                        }
                    }
                }
            }
        }

        /*
         * Also support simulated ignition key.
         *
         * On the test bench, with only an IKE, and no GM or LCM, the IKE emits a repeated ignition key 0 when
         * the switched power (fuse 30?) is on. I tried to inject a message with key position 1, but what
         * happens is that it's spammed out from the IKE. Here, we listen for simulated ignition key events,
         * then set a flag to ignore real ones until the pico restarts.
         */

        if (iBusPacket->getSourceDevice() == PI_VALUE && iBusPacket->getDestinationDevice() == PICO_VALUE) {
            messages::PiToPicoMessage decoded = decodePiToPicoMessage(logger, *iBusPacket);
            if (decoded.messageType == messages::PiToPicoMessage::MessageType::SimulatedIgnitionPosition0 ||
                decoded.messageType == messages::PiToPicoMessage::MessageType::SimulatedIgnitionPosition1 ||
                decoded.messageType == messages::PiToPicoMessage::MessageType::SimulatedIgnitionPosition2 ||
                decoded.messageType == messages::PiToPicoMessage::MessageType::SimulatedIgnitionPosition3) {

                //logger->i(getTag(), "Received a simulated ignition key message");
                ignoreFutureRealIgnitionEmissions = true;

                switch (decoded.messageType) {
                    case messages::PiToPicoMessage::MessageType::SimulatedIgnitionPosition0:
                        onIgnitionKeyPosition(0);
                        break;
                    case messages::PiToPicoMessage::MessageType::SimulatedIgnitionPosition1:
                        onIgnitionKeyPosition(1);
                        break;
                    case messages::PiToPicoMessage::MessageType::SimulatedIgnitionPosition2:
                        onIgnitionKeyPosition(2);
                        break;
                    case messages::PiToPicoMessage::MessageType::SimulatedIgnitionPosition3:
                        onIgnitionKeyPosition(3);
                        break;
                    default: break;
                }
            }
        }

    }

Video and NoVideo observers

The Pico firmware can be compiled without video support as well. The central difference is that on Ignition Key 1, the video switch and scan program is set to a loading screen to hide the RPi bootup process.

case 1:
    //Turn the pi on
    pi4PowerSwitchManager->setPower(true);
    videoSwitch->switchTo(hardware::videoSwitch::VideoSource::PICO);
    scanProgramSwapper->swapTo(ScanProgram::CLOCK);
    screenPowerManager->sendScreenPowerMessage(true);

Once the HMI is booted up, the e39-rpi can send an IBus message to set the video switch to VideoSource::PI.

Return to Top