A Controller Area Network (CAN Bus) is a vehicle bus standard designed to allow microcontrollers and devices to communicate with each other in applications without a host computer. It is a message-based protocol, designed originally for multiplex electrical wiring within automobiles, but is also used in many other contexts.
The ConnectCore 8X has several CAN ports to communicate with other devices using this protocol. In the ConnectCore 8X Hardware Reference Manual, you can find information about the available CAN channels.
Digi adds to Android an API to manage these CAN interfaces. You can configure the CAN settings, write, read, and add listeners among other things. In the Digi APIx javadoc you can find a complete list of the available methods in this API.
Unless noted, all CAN API methods require the com.digi.android.permission.CAN
permission.
If your application does not have the com.digi.android.permission.CAN permission it will not have access to any CAN service feature.
|
First, a new CANManager
object must be instantiated by passing the Android Application Context.
import android.app.Activity;
import android.os.Bundle;
import com.digi.android.can.CANManager;
public class CANSampleActivity extends Activity {
CANManager canManager;
[...]
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Instantiate the CAN manager object.
canManager = new CANManager(this);
[...]
}
[...]
}
List CAN interfaces
The CANManager
allows you to list the available CAN interfaces in the system.
import com.digi.android.can.CANManager;
[...]
CANManager canManager = ...;
// List available CAN interfaces
int[] interfaces = canManager.listInterfaces();
// Print available interfaces
for (int i = 0; i < interfaces.length; i++)
System.out.format("can%d", interfaces[i]);
[...]
Open/close a CAN interface
The CANManager
allows you to create a CAN
object for a specific CAN interface.
import com.digi.android.can.CAN;
import com.digi.android.can.CANManager;
[...]
CANManager canManager = ...;
// Create CAN object for interface number 0.
CAN can0 = canManager.createCAN(0);
To work with a CAN interface, you must open it so you can read and write data. To manage the interfaces use the following CAN class methods:
Method | Description |
---|---|
|
Opens the CAN interface |
|
Closes the CAN interface |
|
Returns the CAN interface status |
open()
and close()
methods may fail for the following reasons:
-
If the interface number represented by the
CAN
object does not exist, theopen()
method throws aNoSuchInterfaceException
. -
If there is any error while opening or closing the CAN interface an
IOException
is thrown.
import com.digi.android.can.CAN;
import com.digi.android.can.CANManager;
[...]
CANManager canManager = ...;
// Create CAN object for interface number 0.
CAN can0 = canManager.createCAN(0);
// Check if the CAN 0 is already opened. If not, open it.
if (!can0.isInterfaceOpen())
can0.open();
[...]
// Close CAN 0.
can0.close();
Configure a CAN interface
Before sending and receiving data, the CAN interface bitrate must be configured. By default, it is set to 500 kbps.
Use the setBitrate(int)
method to change the bitrate of a CAN interface.
This method may fail if:
-
The CAN interface does not support bitrate changes. In this case a
UnsupportedOperationException
is thrown. -
Any error occurs while setting the new bitrate, then an
IOException
is thrown.
import com.digi.android.can.CAN;
import com.digi.android.can.CANManager;
[...]
CANManager canManager = ...;
CAN can0 = ...;
[...]
// Set the new bitrate.
can0.setBitrate(125000);
[...]
Send data
Once the bitrate is properly configured, you can send data through the CAN interface.
The CAN API includes a class called CANFrame
to represent the data over the CAN bus.
To instantiate a CAN frame, you need to provide the following parameters:
-
A
CAN
object to send the frame. -
A
CANId
object, the identifier of the frame. -
The data to send.
import com.digi.android.can.CAN;
import com.digi.android.can.CANFrame;
[...]
CAN can0 = ...;
CANId id = new CANId(50, false);
byte[] dataToSend = new byte[]{'D', 'a', 't', 'a', ' ', 't', 'o', ' ', 's', 'e', 'n', 'd'};
// Instantiate a new frame to send.
CANFrame frame = new CANFrame(can0, id, dataToSend);
[...]
To send the data, use the write(CANFrame)
method with the CANFrame
object that contains the data to be transmitted.
This method may fail if the interface is closed or if any error occurs while transmitting the data throwing an IOException
.
import com.digi.android.can.CAN;
import com.digi.android.can.CANFrame;
[...]
CAN can0 = ...;
CANId id = ...;
byte[] dataToSend = ...;
if (!can0.isInterfaceOpen())
can0.open();
[...]
CANFrame frame = new CANFrame(can0, id, dataToSend);
// Transmit the data.
can0.write(frame);
[...]
When you are done with the CAN interface you need to close it by calling the close() method.
|
Receive data
To receive data you must:
Once you received all the required data, you can stop the reading process and unregister the listener.
Register for frame notifications
To receive data you must subscribe an ICANListener
to the CAN
object that represents the interface where you are going to read data.
Use the registerListener(ICANListener)
method to register for the notification of new CAN frames notifications.
import com.digi.android.can.CAN;
import com.digi.android.can.CANFrame;
[...]
CAN can0 = ...;
// Create the CAN listener.
MyCANListener myCANListener = ...;
// Register the CAN listener.
can0.registerListener(myCANListener);
[...]
The registered listener class, MyCANListener
, must implement the ICANListener
interface.
This interface defines the frameReceived(CANFrame)
method that is called every time a CAN frame is received.
import com.digi.android.can.CANFrame;
import com.digi.android.can.ICANListener;
public class MyCANListener implements ICANListener {
@Override
public void frameReceived(CANFrame frame) {
System.out.println("Received on CAN " + frame.getCAN().getInterfaceNumber()
+ ": " + new String(frame.getData()));
}
}
Start the data reception
The listener starts receiving frames as soon as the startRead(List<CANFilter>)
method is called with the list of filters specified.
This allows you to listen only to specific CAN identifiers and masks.
The listener processes each received CAN frame through all configured filters (ID/mask pairs) to remove undesired messages. The listener uses the filter mask to determine which received CAN frame identifier bits to compare to the filter ID:
-
If a mask bit is set to a zero, the corresponding CAN frame ID bit won’t be checked against the filter ID. It will automatically be accepted, regardless of the value of the filter bit.
-
If a mask bit is set to a one, the corresponding CAN frame ID bit will be compared with the value of the filter ID bit. If they match, it is accepted; otherwise, the frame is rejected.
For example, for a filter ID of 0x100 and mask of 0xFF, the CAN IDs 0x100, 0x200, 0x300 will be allowed, as the bits 8 and 9 of the mask are not enabled. However, using the same filter ID and mask, CAN IDs such as 0x101, 0x201, 0x301 will be rejected, as the bit 0 of the mask is set to one and the bit 0 of the CAN frame IDs do not match the bit 0 of the filter ID. A mask value of 0x00 in a filter leaves the filter without effect, as no bits will be tested and all CAN frames will go through.
When working with CAN IDs and filter IDs, if any ID exceeds the 0x7FF value (which is the standard CAN ID length of 11 bits), the 'use extended ID' flag must be set to true while creating the
|
import com.digi.android.can.CAN;
import com.digi.android.can.CANId;
import com.digi.android.can.CANFilter;
[...]
CAN can0 = ...;
MyCANListener myCANListener = ...;
[...]
can0.registerListener(myCANListener);
// Define filters.
ArrayList<CANFilter> filters = new ArrayList<CANFilter>();
CANId id = new CANId(0x50, false);
CANFilter f = new CANFilter(id, 0x7FF); // Only accept messages with ID = 0x50.
filters.add(f);
// Start reading for frames on CAN 0.
can0.startRead(filters);
[...]
The startRead(List<CANFilter>)
method throws an IOException
if the CAN interface is closed.
Stop the data reception
You can have more than one ICANListener
waiting for frames in the same CAN interface.
To stop all CAN interface notifications, use the stopRead()
method.
If you no longer wish to receive CAN frames in a determined listener, use the unregisterListener(ICANListener)
method to unsubscribe an already registered listener.
[...]
CAN can0 = ...;
MyCANListener myCANListener = ...;
ArrayList<CANFilter> filters = ...;
[...]
can0.registerListener(myCANListener);
can0.startRead(filters);
[...]
// Stop reading from CAN 0.
if (can0.isReading())
can0.stopRead();
// Remove the CAN listener.
can0.unregisterListener(myCANListener);
[...]
The stopRead()
method throws an IOException
if the CAN interface is closed.
When you are done with the CAN interface you need to close it by calling the close() method.
|
CAN example
The CAN Sample Application demonstrates the usage of the CAN API. In this example, you can configure all the settings of the CAN interface, write, and read data from the port.
You can import the example using Digi’s Android Studio plugin. For more information, see Import a Digi sample application. To look at the application source code, go to the GitHub repository.