Firmware

Ocelot Firmware guide

Panda Firmware

Panda firmware should run as-is and the board will be detected as a Black Panda. Compatible firmware is available from https://github.com/retroPilot/panda

Ocelot Firmware

Ocelot Firmware draws heavily from Panda and Pedal, but is mainly used for CAN interception. We'll cover a few example usecases, then dive into building our own custom firmware.

Environment setup

Before we begin, make sure your environment is setup to compile and flash Panda firmware. You can follow the setup guide at https://github.com/RetroPilot/panda and then git clone the ocelot repo to your computer from https://github.com/RetroPilot/ocelot/.

iBooster Gateway

The first usecase for Ocelot firmware was to retrofit iBoosters into older vehicles, allowing them to achieve brake-by-wire for use with RetroPilot/openpilot.

Hardware should be connected so that the iBooster's Vehicle CAN is on Bus 2 of the Ocelot and Yaw CAN is on Bus 3 of the Ocelot. Bus 1 of the Ocelot is used to accept the RetroPilot Brake Command message, and the Ocelot will output information from the iBooster on this bus as well. Information on wiring can be found here: https://www.evcreate.nl/wiring-the-tesla-ibooster/ https://www.evcreate.nl/ibooster-can-bus/

Once everything is connected, the firmware can be flashed. Enter DFU on the Ocelot, then from the panda folder: cd board/ibst ./recover.sh

The Ocelot should now be flashed with iBooster Gateway Firmware. Using joystick controls with openpilot [todo: links to joystick controls/op setup] you should be able to command the iBooster on a bench.

SmartDSU

Setup and Flashing

Since Ocelot is a CAN gateway, it is also capable of filtering and modifying messages on-the-fly. This means it's capable of being used as a SmartDSU, filtering out the 0x343 ACC control message in 2017-2019 Corollas, Priuses, RAV4s and other DSU-equipped Toyotas. Wiring is simple enough:

Vehicle CAN from the car -> BUS1 on Ocelot DSU Vehicle CAN -> BUS3 on Ocelot

This configuration seperates the DSU from the vehcile PTCAN, and the firmware takes care of modifying the control message when the car boots up. To flash the firmware, enter the `panda` folder. With the Ocelot in DFU mode: cd board/smart_dsu ./recover.sh

Firmware Explanation

Now, let's step though the firmware to see how it works: https://github.com/RetroPilot/ocelot/blob/main/firmware/smart_dsu/main.c

Most of the magic happens inside CAN3_RX0_IRQ_Handler, where the DSU is sending its CAN frames and being intercepted by Ocelot.

First, the forwarding buffers are setup and filled with the incoming CAN packet data. This ensures that we only modify necessary messages and leave everything else untouched.

  while ((CAN3->RF0R & CAN_RF0R_FMP0) != 0) {

    CAN_FIFOMailBox_TypeDef to_fwd;
    to_fwd.RIR = CAN3->sFIFOMailBox[0].RIR | 1; // TXQ
    to_fwd.RDTR = CAN3->sFIFOMailBox[0].RDTR;
    to_fwd.RDLR = CAN3->sFIFOMailBox[0].RDLR;
    to_fwd.RDHR = CAN3->sFIFOMailBox[0].RDHR;

    uint16_t address = CAN3->sFIFOMailBox[0].RIR >> 21;
    
    #ifdef DEBUG_CAN
    puts("CAN2 RX: ");
    puth(address);
    puts("\n");
    #endif
    
    // CAN data buffer
    uint8_t dat[8];

Next, we look at the incoming message address and decide whether it should be modified with a switch case. Let's take a look at the 0x343 ACC Control message as an example. If we want to disable the low-speed lockout, a single bit in that message must be set to 1 so the PCM can disable the lockout and the car can brake to stop. First, the data must be stored into the buffer:

          for (int i=0; i<8; i++) {
            dat[i] = GET_BYTE(&CAN3->sFIFOMailBox[0], i);
          }

Next, the buffer can be modified by bitwise operations and the checksum recomputed:

            // add permit_braking and recompute the checksum
            dat[2] &= 0x3F; // mask off the top 2 bits
            dat[2] |= (1 << 6U); // SET_ME_X01
            dat[3] |= (1 << 6U); // permit_braking
            dat[7] = toyota_checksum(address, dat, 8); 

And finally, the resulting buffer can be sent into the forwarding queue:

            to_fwd.RDLR = dat[0] | (dat[1] << 8) | (dat[2] << 16) | (dat[3] << 24);
            to_fwd.RDHR = dat[4] | (dat[5] << 8) | (dat[6] << 16) | (dat[7] << 24);

Last updated