Pre
Before doing anything we need to:
- Flash the SD card with the software, that is in the linux computer from work
- Check that the CAN interface works
- Install python-can, apt-get install python-can
- and install construct, pip install construct
Post
- Create the systemd service that enable the CAN interface
- Create the systemd that launch the script
Scripts
In /etc/systemd/system/obc_startup.service
:
[Unit]
Description=Send sample CAN messages on startup
Wants=can_enable.service
After=multi-user.target can_enable.service
[Service]
Type=simple
ExecStart=/home/ubuntu/scripts/venv/bin/python3 /home/ubuntu/scripts/startup.py
[Install]
WantedBy=multi-user.target
In /etc/systemd/system/can_enable.service
:
[Unit]
Description=Setup SocketCAN interface can0 with a baudrate of 500000
After=multi-user.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/sbin/ip link set can0 up type can bitrate 500000
ExecStart=/sbin/ifconfig can0 up
ExecStop=/sbin/ifconfig can0 down
[Install]
WantedBy=multi-user.target
And then in /home/ubuntu/scripts/startup.py
:
"""
This example receive the telemetry from a dummy sensor and encapsulate it on a CCSDS packet to send it with Lora with the COMM SS
"""
import time
import can
from construct import BitStruct, BitsInteger, Padding, Struct, Float32l
from construct import Int16ul, Int8ul
CAN_MAX_LENGTH = 8 # Max number of bytes in a message of CAN
CFP_BEGIN = 0b0
CFP_MORE = 0b1
# CFP Some variables
SOURCE = 0
DESTINATION = 1
ID = 63 # Sample id
# Data structures
CAN_CFP_ID = BitStruct(
Padding(3),
# Padding of 3 bits to be multiple of 8,
# discarted when adding to can message
"source" / BitsInteger(5),
"destination" / BitsInteger(5),
"type" / BitsInteger(1),
"remain" / BitsInteger(8),
"id" / BitsInteger(10)
)
BME_DATA = Struct(
"temp" / Float32l,
"preassure" / Float32l,
"altitude" / Float32l,
"humidity" / Float32l
)
CCSDS_primary_header = BitStruct(
"version" / BitsInteger(3),
"type" / BitsInteger(1),
"sec_header_flag" / BitsInteger(1),
"proc_id" / BitsInteger(11),
"seq_flags" / BitsInteger(2),
"seq_cnt" / BitsInteger(14),
"length" / BitsInteger(16)
)
ccsds_header_dict = dict(
version = 0b1,
type = 0b0,
sec_header_flag = 0b0,
proc_id = 0b1,
seq_flags = 0b11,
seq_cnt = 0b0,
length = 15
)
CCSDS_sample_packet = Struct(
"header" / CCSDS_primary_header,
"data" / BME_DATA
)
# Define CAN bus
bustype = 'socketcan'
channel = 'can0'
bus = can.Bus(channel=channel, interface=bustype)
def sendBME_CCSDS(bme_data):
data_parsed = BME_DATA.parse(bytes(bme_data))
packet = CCSDS_sample_packet.build(dict(
header = ccsds_header_dict,
data = data_parsed
))
data = packet
# Create a message and send it using CFP
num_packets = (len(data) - 1) // CAN_MAX_LENGTH + 1
remain = num_packets - 1
# Send the first frame
cfp_id_dict = dict(
source = SOURCE,
destination = DESTINATION,
type = CFP_BEGIN, # BEGIN
remain = remain,
id = ID # Sample identifier
)
cfp_id = CAN_CFP_ID.build(cfp_id_dict)
start = 0 # We start at 0
end = min(start + CAN_MAX_LENGTH, len(data))
print("Sending from {} to {}, remain {}".format(start, end, remain))
# Send first packet
msg = can.Message(arbitration_id=int.from_bytes(cfp_id, 'big'), data=data[start:end], is_extended_id=True)
bus.send(msg)
while(remain > 0):
remain -= 1 # We are sending a new one
# Send the first frame
cfp_id_dict = dict(
source = SOURCE,
destination = DESTINATION,
type = CFP_MORE, # BEGIN
remain = remain,
id = ID # Sample identifier
)
cfp_id = CAN_CFP_ID.build(cfp_id_dict)
start = end
end = min(start + CAN_MAX_LENGTH, len(data))
print("Sending from {} to {}, remain {}".format(start, end, remain))
# Send first packet
msg = can.Message(arbitration_id=int.from_bytes(cfp_id, 'big'), data=data[start:end], is_extended_id=True)
bus.send(msg)
# Given a buffer in bytes print the data
def printBME(buff):
data_parsed = BME_DATA.parse(bytes(buff))
print("Temperature: {}\nPreassure: {} \nAltitude: {}\n Humidity: {}\n".format(
data_parsed.temp,
data_parsed.preassure,
data_parsed.altitude,
data_parsed.humidity
))
def readCFP():
if(True): # There are elements in the queue
# Receive the first packet
msg = bus.recv()
# Process the id
rec_cfp_id = msg.arbitration_id
rec_cfp_parse = CAN_CFP_ID.parse(rec_cfp_id.to_bytes(4, "big"))
print("""Received: \nsource: {}\ndestination: {}\ntype: {}
remain: {}\nid: {}""".format(rec_cfp_parse.source, rec_cfp_parse.destination,
rec_cfp_parse.type, rec_cfp_parse.remain, rec_cfp_parse.id))
remain = rec_cfp_parse.remain
num_packets = remain + 1
# Create a buffer with the info of the packet
buff = [0] * (msg.dlc + remain * CAN_MAX_LENGTH)
print("Creating buffer of {} values".format(msg.dlc + remain * CAN_MAX_LENGTH))
start = 0
end = min(start + CAN_MAX_LENGTH, msg.dlc)
buff[start:end] = msg.data
print("Reading from {} to {}, remain {}".format(start, end, remain))
while(remain > 0):
# Receive the first packet
msg = bus.recv()
# Process the id
rec_cfp_id = msg.arbitration_id
rec_cfp_parse = CAN_CFP_ID.parse(rec_cfp_id.to_bytes(4, "big"))
print("""Received: \nsource: {}\ndestination: {}\ntype: {}
remain: {}\nid: {}""".format(rec_cfp_parse.source, rec_cfp_parse.destination,
rec_cfp_parse.type, rec_cfp_parse.remain, rec_cfp_parse.id))
remain = rec_cfp_parse.remain
start = end
end = min(start + CAN_MAX_LENGTH, start + msg.dlc)
print("Reading from {} to {}, remain {}".format(start, end, remain))
buff[start:end] = msg.data
# Remove the unnesesary part of the buffer because is not filled
buff = buff[0:((num_packets-1)*CAN_MAX_LENGTH) + msg.dlc]
return buff
while True:
buff = readCFP()
printBME(buff)
sendBME_CCSDS(buff)
Enabling the scripts
# systemctl start /etc/systemd/system/can_enable.service
# systemctl enable can_enable.service