Advanced car to USB-serial interface for Arduino
This is an Arduino (Nano) library to allow a serial device to connect to a car. It provides scaffolding and helpers to easily create a customized firmware for your micro controller. The custom firmware can read data from the CAN-bus of the car.
The data read from various sources (pins, CAN-bus, ...) is automatically translated into a standardized communication protocol for data exchange between the arduino and its serial host.
An example of a serial host is CarDroid. It is an android app, that is able to exchange data with Carduino. It can display various metrics from your car on the android device and allows controlling some systems of the car.
Please make sure to read the requirements!
Carduino was created as an interface to allow the app CarDroid to communicate with a nissan 370z. Main goal was to replace the entertainment system with an Android tablet, while keeping the climate controls accessible within Android.
- An MCP2515 SPI board for can access.
Currently no releases, you have to build the project From source
- Get your favourite Arduino IDE
- Clone the repository into your library directory
git clone https://github.com/rampage128/carduino.git
- Start a new sketch and add the library to it
After importing #import <carduino.h>
, you can use the following
features:
The serial device connected to your Arduino can send serial events. These can trigger actions on your Arduino. To do so, you have to add a callback to your sketch:
void onCarduinoSerialEvent(uint8_t eventId, BinaryBuffer * payloadBuffer) {
[...]
}
This callback will be called whenever a user event is sent from the other
serial device to your Arduino. The callback allows you to identify the type of
event with the eventId
parameter. And optional payload (data) for that
event is provided as a BinaryBuffer
in the payloadBuffer
pointer.
BinaryBuffer
is a data-structure that allows you to read data-types
sequentially from it as if it was a stream.
Make sure to attach you callback to Carduino:
Carduino carduino(&Serial, onCarduinoSerialEvent);
Finally whenever you get serial data (for example in your serialEvent()
function), you can call:
carduino.readSerial();
This let's Carduino know, that there is new serial data available.
Car systems reflect different parts of your car. It can be the climate controls, engine, drive-train and many more. They provide a standardized communication-layer from your arduino to your Android device. Each car system contains a data structure, which describes the state of the system in question.
Car systems can directly be manipulated and serialized to the connected serial device. Carduino also provides scaffolding that allows you to easily manipulate these states. Changes are then automatically detected by the library and sent to the connected serial device.
As example see ClimateControl
:
Field Name | Data type | Description |
---|---|---|
isAcOn | Bit | Flags if air conditioning is active |
isAuto | Bit | Flags if automatic controls are active |
isAirductWindshield | Bit | Flags if windshield airduct is active |
isAirductFace | Bit | Flags if direct airduct is active |
isAirductFeet | Bit | Flags if lower airduct is active |
isWindshieldHeating | Bit | Flags if the windshield heating is active |
isRearWindowHeating | Bit | Flags if the rear window heating is active |
isRecirculation | Bit | Flags if air recirculation is active |
fanLevel | 8-Bit unsigned int | Stores the level of the fans |
desiredTemperature | 8-Bit unsigned int | Stores the user selected temperature |
The Can
class allows easy access to the vehicles CAN-Bus. It provides
helpers and scaffolding to easily read data and automatically sends changes in
a car-system to the connected serial device. It uses SPI to communicate with a
MCP2515 can module.
Create a Can
object in your sketch:
Can can(&Serial, 2, 10);
(In this example 2 is the interrupt pin and 10 is the client select pin of the SPI setup).
In the setup()
function you can initialize the can-connection and inform
Carduino
about your Can
object.
can.setup(MCP_ANY, CAN_500KBPS, MCP_8MHZ);
carduino.addCan(&can);
(The constants are taken from MCP_CAN_lib)
After having initialized the CAN system, it can be used in the loop()
function of your sketch in four steps:
- Add a callback function to your sketch to read the CAN-data:
The callback function can have any name you like and provides the CAN-ID, the length of the CAN-packet, the CAN-data in an array and a pointer to the car system. Any changes applied to the car system will automatically be detected by Carduino and sent to the connected serial device. (only if something actually changed!)
void updateClimateControl(long unsigned int id, unsigned char len, unsigned char data[8], ClimateControl * climateControl) { [...] }
- Begin a CAN-transaction by calling:
can.beginTransaction();
- Associate a car system to a CAN-ID and a callback function:
In this case the
can.updateFromCan<ClimateControl>(0x54A, climateControl, updateClimateControl);
ClimateControl
car system is associated to the CAN-ID0x54A
. Once the CAN-Module receives this CAN-ID, it calls the callback functionupdateClimateControl
. - End the transaction:
can.endTransaction();
Please note: You can add as many associations as you like within one transaction. Carduino is smart and only calls your callback functions if a matching CAN-ID is received.
The Arduino sends and receives serial packets to the USB-Serial interface.
Easiest way to make real use of the data is to connect an Android device to the Arduino's USB-Port and get CarDroid.
The hard way is to read the packets from the serial connection. The protocol is custom and subject to change. Generally data is sent in frames. Each frame contains one single packet.
Please see the table below for a general idea:
Index | Length | Possible values | Description |
---|---|---|---|
0 | 1 | 0x7b |
Start of a frame |
1 | 1 | 0x00 - 0xFF |
Packet type to indicate purpose |
2 | 1 | 0x00 - 0xFF |
Packet id to indicate request |
3 | 1 | 0x01 - 0x7c |
Payload length (L) (only if payload present) |
4 | (1 - 124) L | 0x01 - 0xFF |
Payload (only if present) |
3 + L | 1 | 0x7d |
End of a frame |
For more information, please refer to the source.
Feel free to open an issue or submit a PR
Also, if you like this or other of my projects, please feel free to support me using the Link below.
- AnalogMultiButton to read the steering wheel remote
- everytime for easy periodic code execution (send packets)
- MCP_CAN_lib to communicate with CAN-Bus on MCP2515
- SPI library to utilize SPI interface
- BitFieldMember to allow usage of ordered bitfields in unions