Cell voltages fork (SoC)

Dependencies:   CUER_CAN CUER_DS1820 LTC2943 LTC6804 mbed PowerControl

main.cpp

Committer:
lcockerton62
Date:
2017-07-10
Revision:
30:d90895e96226
Parent:
29:44924d2b1293
Child:
31:888b2602aab2

File content as of revision 30:d90895e96226:

#include "mbed.h"
#include "CANParserBMU.h"
#include "Data_Types_BMU.h"
#include "CAN_Data.h"
#include "CAN_IDs.h"
#include "EEPROM_I2C.h"
#include "Temperature.h"
#include "LTC2943_Read.h"
#include "Cell_Voltage.h"
#include "SPI_I2C_Parser.h"
#include "LTC2943.h"


using namespace CAN_IDs;

// Function definitions
void transmit_data(BMU_data measurements,uint32_t status);
void read_temperature_sensors(BMU_data &measurements);
void update_SOC();
void init();
void interruptHandler();
void CANDataSentCallback();
void write_SOC_EEPROM(BMU_data &measurements,uint16_t start_address);
uint16_t read_EEPROM_startup(BMU_data &measurements);
uint32_t check_measurements(BMU_data &measurements);
uint32_t take_measurements(BMU_data &measurements);
void test_read_CAN_buffer();
bool test_read_voltage_CAN(uint16_t readings[], int can_ids[]);
void test_CAN_send();
void test_CAN_read();
bool check_EEPROM_PEC( uint16_t start_address, char start_address_array[], char SOC_out[]);

CAN can(CAN_READ_PIN, CAN_WRITE_PIN); //Create a CAN object to handle CAN comms
CANMessage buffer[CAN_BUFFER_SIZE]; //CAN receive buffer
bool safe_to_write[CAN_BUFFER_SIZE]; //Semaphore bit indicating that it's safe to write to the software buffer
bool CAN_data_sent = false;

//Global array to store most recently obtained voltage and temp measurement:
CMU_voltage voltage_readings[NO_CMUS];
individual_temperature templist[NO_TEMPERATURE_SENSORS];
uint32_t status;
//LTC2943 ltc2943(i2c_sda, i2c_scl, alcc_pin, &dummyfunction, R_SENSE, BATTERY_CAPACITY);

uint16_t eeprom_start_address; //the initial address where we store/read SoC values

Timeout loop_delay;
bool delay_finished = false;

//The following is to initialize reading tests, can be removed when needed
float packSOC;
float packSOCPercentage;
pack_voltage_extremes minVolt;
pack_voltage_extremes maxVolt;
pack_temperature_extremes minTemp;
pack_temperature_extremes maxTemp;

void loop_delay_callback(void)
{
    delay_finished = true;
}

int main()
{ 
    BMU_data measurements;
    uint16_t current_EEPROM_address;
    uint16_t volt_readings[36];
    int can_ids[9];
    
    init();
    
    //current_EEPROM_address = read_EEPROM_startup(measurements); // Read from the eeprom at startup to fill in the values of SoC
    //ltc2943.accumulatedCharge(measurements.percentage_SOC); // Initialise the LTC2943 with the current state of charge
    
    while (true) {
        
        //status = take_measurements(measurements);
        /*// Dont want to read the temperature sensors during each iteration of the loop

        //Store data in the eeprom
        write_SOC_EEPROM(measurements, current_EEPROM_address);
        */
        // CAN bus
        CAN_data_sent = false;//Currently does nothing, adding this line in more places then using
        //while(!CAN_data_sent); in order to ensure sending completes
        //transmit_data(measurements,status);
        test_read_CAN_buffer();
        
        /*
        // Conserve power - enter a low powered mode
        delay_finished = false;
        loop_delay.attach(loop_delay_callback, LOOP_DELAY_S);
        while (!delay_finished) sleep();
        */
        wait(1);
    } 
}

void transmit_data(BMU_data measurements, uint32_t status)
{
    CANMessage msg;
    /*
    Place all of the collected data onto the CAN bus
    */
    // Send cell voltages
    //voltages sent in sets of 4 + one cmu data set
    int repeating_unit_length = NO_READINGS_PER_CMU /4 + 1;
    for(uint16_t i= 0; i < NO_CMUS; i++) {
        //input id is offset, data structure is info, voltage, voltage, ......
        //This is a slightly modified version of the Tritium BMS datasheet, to add an extra voltage reading set.
        msg = createVoltageTelemetry(repeating_unit_length*i+2, measurements.cell_voltages[i].voltages); 
        can.write(msg);
        printf("Voltage Message id: %d \r\n", msg.id);
        //+4 - 4 cell voltages sent per measurement, simple pointer arithmetic
        msg = createVoltageTelemetry(repeating_unit_length*i+3, measurements.cell_voltages[i].voltages + 4); 
        can.write(msg);
        printf("Voltage Message id: %d \r\n", msg.id);
        msg = createVoltageTelemetry(repeating_unit_length*i+4, measurements.cell_voltages[i].voltages + 8); 
        can.write(msg);
        printf("Voltage Message id: %d \r\n", msg.id);
    }
    
    //Transmitting all of the individual probes:
    for(uint8_t i = 0; i < devices_found; i++)
    {
        individual_temperature tempreading = measurements.temperature_measurements[i];
        msg = createTemperatureTelemetry(i, &tempreading.ROMID[0], tempreading.measurement);
        individual_temperature testOut = decodeTemperatureTelemetry(msg);
        printf("Temperature reading sent (CAN ID = %d): (%f,%d) \r\n", msg.id, testOut.measurement, testOut.ID); 
        if(can.write(msg));
        else
            printf("Sending Temperature Failed for some reason");
    }

    // Create SOC CAN message
    msg = createPackSOC(measurements.SOC, measurements.percentage_SOC);
    can.write(msg);
    printf("SOC is %f and percentage SOC is %f and id is %d \r\n", measurements.SOC, measurements.percentage_SOC, msg.id);
    
    // Min/max cell voltages
    msg = createCellVoltageMAXMIN(measurements.max_cell_voltage, measurements.min_cell_voltage);
    can.write(msg);
    
    // Min/Max cell temperatures
    msg = createCellTemperatureMAXMIN(measurements.min_cell_temp, true);
    can.write(msg);
    msg = createCellTemperatureMAXMIN(measurements.max_cell_temp, false);
    can.write(msg);
    
    // Battery voltage and current
    // @TODO add the voltage
    msg = createBatteryVI(measurements.battery_voltage,measurements.battery_current);
    //can.write(msg);
    
    //Extended battery pack status
    msg = createExtendedBatteryPackStatus(status);
    can.write(msg);
}


uint16_t read_EEPROM_startup(BMU_data &measurements)
{
    /* The first page of the EEPROM, specifically the first 2 addresses store a
    pointer of the first memory location of measurement data. The EEPROM only has a finite number of
    read/write cycles which is why we aren't writing to the same location throughout
    */
    uint16_t start_address1;
    uint16_t start_address2;
    uint16_t start_address;
    char start_address_array1[2];
    char start_address_array2[2];
    char SOC_out[10]; // 4 bytes for the 2 floats one is SOC and the other % charge
    float *fp1,*fp2; // temporary storage for float conversion
    bool is_first_read_true = 0;
    bool is_second_read_true = 0;

    // Get a pointer to the start address for the data stored in the eeprom
    i2c_page_read(0x0000,2,start_address_array1);
    i2c_page_read(0x0002,2,start_address_array2);
    
    is_first_read_true = check_EEPROM_PEC(start_address, start_address_array1, SOC_out);
    
    if(is_first_read_true){
        fp1 = (float*)(&SOC_out[0]);
        fp2 = (float*)(&SOC_out[4]);
        measurements.SOC = *fp1;
        measurements.percentage_SOC = *fp2;
    }
    else{
        is_second_read_true = check_EEPROM_PEC(start_address, start_address_array2, SOC_out);
        
        if(is_second_read_true){
            fp1 = (float*)(&SOC_out[0]);
            fp2 = (float*)(&SOC_out[4]);
            measurements.SOC = *fp1;
            measurements.percentage_SOC = *fp2;
        }
    }    
    
    if(is_second_read_true || is_first_read_true){
        // Select the next address to write to
        start_address1 += 0x0040;
        start_address2 += 0x0040;
        if(start_address > MAX_WRITE_ADDRESS) {
            start_address1 = START_WRITE_ADDRESS;        // Loop to the start of the eeprom
            start_address2 = START_WRITE_ADDRESS + SECOND_ADDRESS_OFFSET;  // Write this data SECOND_ADDRESS_OFFSET memory locations later than the first set                                                   //  (this was chosen since only 10 bytes are written to memory
        }
        start_address_array1[0] = start_address1 | 0x00FF;
        start_address_array1[1] = start_address1 >> 8;
        start_address_array2[0] = start_address2 | 0x00FF;
        start_address_array2[1] = start_address2 >> 8;
        
        // Write the new location of the address to memory
        i2c_page_write(0x0000, 2, start_address_array1);
        i2c_page_write(0x0002, 2, start_address_array2);
    
        return start_address1;
    }
    else{
        printf("PEC error"); //@TODO an error flag should be raised since both values have failed
    }
}

bool check_EEPROM_PEC( uint16_t start_address, char start_address_array[], char SOC_out[]){
    // Helper method to check the PEC, returns 0 if the pec is wrong and 1 if the pec is correct
    uint16_t received_pec;
    uint16_t data_pec;
    
    // Read the data from this address
    start_address = (start_address_array[1]<< 8) | start_address_array[0]; // mbed little endian follow this convention
    i2c_page_read(start_address, 10,SOC_out); // Reading will aquire 2 floats and a PEC for the data

    // Convert the SOC_out values back into floats and deal with the pec
    received_pec = (uint16_t)(SOC_out[8]<<8) + (uint16_t)SOC_out[9];
    data_pec = pec15_calc(8, (uint8_t*)SOC_out);
    if(received_pec != data_pec) {
         return 0;
    }
    else
        return 1;
}

void write_SOC_EEPROM(BMU_data &measurements,uint16_t start_address)
{
    char data_out[10];
    float *fp1,*fp2;
    uint16_t data_pec; 

    fp1 = (float*)(&measurements.SOC);
    fp2 = (float*)(&measurements.percentage_SOC);

    for(int i = 0; i < 4; i++ ) {
        data_out[i] = *fp1;
        fp1++;
    }
    for(int j = 4; j < 7; j++ ) {
        data_out[j] = *fp2;
        fp2++;
    }
    data_pec = pec15_calc(8, ((uint8_t*)data_out)); // Calculate the pec and then write it to memory
    data_out[8] = (char)(data_pec >> 8);
    data_out[9] = (char)(data_pec);
    i2c_page_write(start_address, 10,data_out);
    i2c_page_write((start_address+SECOND_ADDRESS_OFFSET), 10,data_out); // Write the data to the backup memory location, SECOND_ADDRESS_OFFSET memory locations later
}

void read_temperature_sensors(BMU_data &measurements)
{
    float min_temperature;
    char min_id[8];
    float max_temperature;
    char max_id[8];
    isotherm_12V_pin = 1;
    probe[0]->convert_temperature(DS1820::all_devices);
    
    min_temperature = probe[0]->temperature('C');
    std::memcpy(min_id, probe[0]->ROM, sizeof(char)*8); //invalid shallow copy: min_id = probe[0]->ROM;
    max_temperature = min_temperature; // Initially set the max and min temperature equal
    std::memcpy(max_id, probe[0]->ROM, sizeof(char)*8);
    for (int i=0; i<devices_found; i++) {
        for(int j = 0; j < 7; j++)
            measurements.temperature_measurements[i].ROMID[j] = probe[i]->ROM[j];
        measurements.temperature_measurements[i].measurement = probe[i] ->temperature('C');
        
        if(measurements.temperature_measurements[i].measurement > max_temperature) {
            max_temperature = measurements.temperature_measurements[i].measurement;
            std::memcpy(max_id, measurements.temperature_measurements[i].ROMID, sizeof(char)*8);
        } else if (measurements.temperature_measurements[i].measurement < min_temperature) {
            min_temperature = measurements.temperature_measurements[i].measurement;
            std::memcpy(min_id, measurements.temperature_measurements[i].ROMID, sizeof(char)*8);
        }
        
        //printf("Device %d temperature is %3.3f degrees Celcius.\r\n",i+1 ,probe[i]->temperature('C'));
    }
    isotherm_12V_pin = 0;
    //There is also a CMU # component of this struct, currently unfilled, perhaps not needed at all.
    measurements.max_cell_temp.temperature = max_temperature;
    std::memcpy(measurements.max_cell_temp.ROMID, max_id, sizeof(char)*8);
    measurements.min_cell_temp.temperature = min_temperature;
    std::memcpy(measurements.min_cell_temp.ROMID, min_id, sizeof(char)*8);
    delete max_id;
    delete min_id;
}

void update_SOC()
{
    // Update the SOC value
    ltc2943.readAll();
}


uint32_t check_measurements(BMU_data &measurements)
{
    uint32_t status;

    if(measurements.max_cell_voltage.voltage > MAX_CELL_VOLTAGE) {
        status = status | CELL_OVER_VOLTAGE;
    } else if (measurements.min_cell_voltage.voltage < MIN_CELL_VOLTAGE) {
        status = status | CELL_UNDER_VOLTAGE;
    } else if (measurements.max_cell_temp.temperature > MAX_CELL_TEMPERATURE) {
        status = status | CELL_OVER_TEMPERATURE;
    }

    /*
    @TODO also include errors for:
    *untrusted measurement
    *CMU timeout
    *SOC not valid
    */
    return status;
}

//Returns the status variable
uint32_t take_measurements(BMU_data &measurements)
{
    uint16_t cellvoltages[NO_CMUS][12];
    //Use LTC6804_acquireVoltage to fill this array, and then properly format
    //it to be sent over CAN
    
    LTC6804_acquireVoltage(cellvoltages);
    pack_voltage_extremes min_voltage;
    pack_voltage_extremes max_voltage; //TODO do minmax voltage stuff
    min_voltage.voltage = 65535; //largest 16 bit unsigned int
    max_voltage.voltage = 0;
    
    //Sets voltage readings as well as max/min voltage values.
    for(int i=0; i<NO_CMUS; i++){
       for(int j=0; j < NO_READINGS_PER_CMU; j++){
             measurements.cell_voltages[i].voltages[j] =  cellvoltages[i][j]/ 10; //To get units of mV
             measurements.cell_voltages[i].CMU_number = i;
             if(measurements.cell_voltages[i].voltages[j] < min_voltage.voltage)
             {
                min_voltage.voltage = measurements.cell_voltages[i].voltages[j];
                min_voltage.CMU_number = i;
                min_voltage.cell_number = j;   
             }
             else if(measurements.cell_voltages[i].voltages[j] < max_voltage.voltage)
             {
                max_voltage.voltage = measurements.cell_voltages[i].voltages[j];
                max_voltage.CMU_number = i;
                max_voltage.cell_number = j;   
             }
       }   
    }
    measurements.max_cell_voltage = max_voltage;
    measurements.min_cell_voltage = min_voltage; 
    
    //Add code to take all temperature measurements and add it to measurements struct.
    read_temperature_sensors(measurements);

    // Update the SOC and take relevant measurements
    update_SOC();
    measurements.battery_current = (uint32_t) ltc2943.current() * 1000; //*1000 to convert to mA
    measurements.percentage_SOC = ltc2943.accumulatedCharge();
    measurements.SOC = (measurements.percentage_SOC /100) * BATTERY_CAPACITY;
    
    // Check data for errors
    return check_measurements(measurements);
}

void init()
{
    //Comment out measurement stuff with BCU testing
    /*temperature_init(); // Initialise the temperature sensors
    LTC2943_initialise(); //Initialises the fixed parameters of the LTC2943
    LTC6804_init(MD_FAST, DCP_DISABLED, CELL_CH_ALL, AUX_CH_VREF2); //Initialises the LTC6804s
    */
    for(int i=0; i<CAN_BUFFER_SIZE; i++) 
    {
        buffer[i].id = BLANK_ID;
        safe_to_write[i]= true;
    }
    
    //Initialise CAN stuff, attach CAN interrupt handlers
    can.frequency(CAN_BIT_RATE); //set transmission rate to agreed bit rate (ELEC-006)
    can.reset(); // (FUNC-018)
    can.attach(&interruptHandler, CAN::RxIrq); //receive interrupt handler
    can.attach(&CANDataSentCallback, CAN::TxIrq); //send interrupt handler
    
    //Initialize voltage array
    for(int i = 0; i < NO_CMUS; i++)
    {
        for(int j = 0; j < NO_READINGS_PER_CMU; j++)
        {
            voltage_readings[i].voltages[j] = 0;   
        }
    }
    //Initialize Temperature Array
    for(int i = 0; i < NO_TEMPERATURE_SENSORS; i++)
    {
        templist[i].measurement = INFINITY;
        templist[i].ID = 0;
    }
    //initialize stuff used in reading test:
    packSOC = INFINITY;
    packSOCPercentage = INFINITY;
    
    minVolt.voltage = 0;
    maxVolt.voltage = 0;
    
    minTemp.temperature = 0; minTemp.ID = 0;
    maxTemp.temperature = 0; maxTemp.ID = 0;
}

void CANDataSentCallback(void) {
    CAN_data_sent = true;
}

void interruptHandler()
{
    CANMessage msg;
    can.read(msg);
    for(int i=0; i<CAN_BUFFER_SIZE; i++) {
        if((buffer[i].id == msg.id || buffer[i].id==BLANK_ID) && safe_to_write[i]) {
           //("id %d added to buffer \r\n", msg.id);
           buffer[i] = msg;
           //return required so that only first blank buffer entry is converted to incoming message ID each time new message ID is encountered
           return;
        }
    }
}

void test_read_CAN_buffer()
{
    //Import the data from the buffer into a non-volatile, more usable format
    CAN_Data can_data[CAN_BUFFER_SIZE]; //container for all of the raw data
    CANMessage msgArray[CAN_BUFFER_SIZE]; //Same as above but some functions take message as their parameter
    int received_CAN_IDs[CAN_BUFFER_SIZE]; //needed to keep track of which IDs we've received so far
    for (int i = 0; i<CAN_BUFFER_SIZE; ++i) 
    {
        safe_to_write[i] = false;
        can_data[i].importCANData(buffer[i]);
        received_CAN_IDs[i] = buffer[i].id;
        msgArray[i] = buffer[i];
        safe_to_write[i] = true;
    }
    
    //voltage and Temp and SOC readings:
    for(int i = 0; i < CAN_BUFFER_SIZE; i++)
    {
        //voltage
        if(decodeVoltageTelemetry(msgArray[i], voltage_readings))
            continue;
        //temperature 
        if(msgArray[i].id >= 0x700)
        {
            individual_temperature dataPoint = decodeTemperatureTelemetry(msgArray[i]);
            for(int j = 0; j < NO_TEMPERATURE_SENSORS; j++)
            {
                if(dataPoint.ID == templist[j].ID)
                {
                    templist[j] = dataPoint;
                    break;
                }
                else if(templist[j].ID == 0)
                {
                    templist[j] = dataPoint;  
                    break;
                }     
            }
            
        }
        //SOC
        if(msgArray[i].id == 0x6F4)
        {
            packSOC = decodePackSOC(msgArray[i]);
            packSOCPercentage = decodePackSOCPercentage(msgArray[i]);
        } 
        
        if(msgArray[i].id == BMS_BASE_ID + MIN_TEMPERATURE)
            minTemp = decodeCellTemperatureMAXMIN(msgArray[i]);
        if(msgArray[i].id == BMS_BASE_ID + MAX_TEMPERATURE)
            maxTemp = decodeCellTemperatureMAXMIN(msgArray[i]);
        
        if(msgArray[i].id == BMS_BASE_ID + MAX_MIN_VOLTAGE)
        {
            decodeCellVoltageMAXMIN(msgArray[i], minVolt, maxVolt);
        }
        
        if(msgArray[i].id == BMS_BASE_ID + BATTERY_STATUS_ID)
            status = decodeExtendedBatteryPackStatus(msgArray[i]); 
    } 
    //Print obtained Readings:
    for(int i = 0; i < NO_CMUS; i++)
        for(int j = 0; j < 12; j++)
            printf("Voltage number %d for CMU %d is %d \r\n", j, i, voltage_readings[i].voltages[j]);
    
    for(int i = 0; i < NO_TEMPERATURE_SENSORS; i++)
        printf("Temperature of Sensor with ID %d is %f \r\n", templist[i].ID, templist[i].measurement);       
        
    printf("SOC is %f and SOC Percentage is %f \r\n", packSOC, packSOCPercentage);  
    
    printf("Voltage (Max,Min) = (%d,%d) \r\n", maxVolt.voltage, minVolt.voltage);
    
    printf("(Temperature, ID): Minimum = (%d,%d). Maximum = (%d,%d) \r\n",
        minTemp.temperature,minTemp.ID,maxTemp.temperature,maxTemp.ID);  
        
    printf("Status value is: %d \r\n", status);
}

bool test_read_voltage_CAN(uint16_t readings[], int can_ids[])
{
    CANMessage msg;
    int can_id;
    int offset;
    int first_index;
    int second_index;
    
    if(can.read(msg))
    {
        for(int i =0; i < 4; i++)
        {
            readings[i] = (msg.data[2 * i]) + (msg.data[2*i+1] << 8); //Since data is 8 8bit ints not 4 16 bit ones
        }
        can_id = msg.id;
        can_ids[0] = msg.id;
        
        offset = can_id - 1536; //1536 = 0x600
        first_index = (offset - 1)/4; //offset of 2,3,4 is CMU 1; 6,7,8, is CMU 2; etc.
        second_index = ((offset - 1) % 4) - 1; //Makes it so 0,1,2 represent each voltage set //SID: subtracted 1 to make it work
        
        return true;
    }
    else
        return false;     
}

void test_CAN_send()
{
    CANMessage msg;
    char value = 142;
    msg = CANMessage(1, &value,1);
    if(can.write(msg))
        printf("Succesfully sent %d \r\n", value);
    else
        printf("Sending Failed \r\n");   
}

void test_CAN_read()
{
    CANMessage msg;
    if(can.read(msg))
        printf("Successfully recieved %d \r\n", msg.data[0]);  
    else
        printf("Reading Failed \r\n"); 
}