- Muchas notas - Fran Acién

20231014 - OBCV2 - Prepare software

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