spacelab-ufsc / eps2

Electrical Power System 2.0
GNU General Public License v3.0
24 stars 5 forks source link

I2C Communication #197

Open MatthewStueber opened 1 month ago

MatthewStueber commented 1 month ago

I am trying to interface from an Arduino Uno (Master) with the EPS 2.0 board (Slave) via I2C. I have flashed the firmware, located on the spacelab-ufsc/eps2 GitHub page, to the EPS 2.0 MCU using the process highlighted in the README file located in the firmware folder. I have connected Dupont wires between the Arduino (A4 (SDA) and A5 (SCL)) to the ESP 2.0 (H2 pin 49 (SDA) and H2 pin 51 (SCL)) respectively. I understand that the Arduino must write an ID (0-48) to the EPS 2.0 then the ESP 2.0 will respond with the return of the ID. The Arduino Code is located below. The problem arises when the Arduino reads from the EPS 2.0. The data being read is different every time I request the Device ID which should return 0xEEE2 (The ID should not change). Please comment where I can find the correct way to interface via I2C, find the I2C packet structure or any suggested changes that should be made to the Arduino Code.

\ Arduino Uno IDE code //

#include <Wire.h>

#define EPS_I2C_ADDRESS 0x36  // EPS's I2C address
#define EPS2_PARAM_ID_DEVICE_ID 48  // Replace with the correct ID for EPS2_PARAM_ID_DEVICE_ID

void setup() {
  Wire.begin();  // Initialize I2C communication
  Serial.begin(9600);  // Initialize serial communication at 9600 baud

  // Writing the ID to the EPS
  uint32_t deviceID = 48;  // ID value you want to write

  for(int i=0; i<3; i++){
    Serial.println(" ");
    writeIDToEPS(EPS2_PARAM_ID_DEVICE_ID, deviceID);

    // Reading the ID back from the EPS
    uint32_t readID = readIDFromEPS(EPS2_PARAM_ID_DEVICE_ID);

    // Print the result to the Serial Monitor
    Serial.println(" ");
    Serial.print("Read Device ID: 0x");
    Serial.println(readID, HEX);
  }

}

void loop() {
  // Empty loop
}

void writeIDToEPS(uint8_t paramID, uint32_t value) {
  Wire.beginTransmission(EPS_I2C_ADDRESS);
  Wire.write(paramID);  // Send the parameter ID to EPS
  Wire.write((uint8_t*)&value, 4);  // Send the 32-bit value (4 bytes) to EPS
  //Wire.endTransmission();  // End transmission to EPS

  //delay(100);  // Small delay to ensure the EPS processes the command
}

uint32_t readIDFromEPS(uint8_t paramID) {
  Wire.beginTransmission(EPS_I2C_ADDRESS);
  Wire.write(paramID);  // Send the parameter ID to request data from EPS

  //delay(100);  // Small delay to allow the EPS to prepare the response

  Wire.requestFrom(EPS_I2C_ADDRESS, 4);  // Request 4 bytes (32-bit data) from EPS
  uint32_t value = 0;
  if (Wire.available() == 4) {
    uint8_t byte1 = Wire.read(); 
    uint8_t byte2 = Wire.read();
    uint8_t byte3 = Wire.read();
    uint8_t byte4 = Wire.read();

    // Print each byte received
    Serial.print("Received bytes: ");
    Serial.print(byte1, HEX); Serial.print(" ");
    Serial.print(byte2, HEX); Serial.print(" ");
    Serial.print(byte3, HEX); Serial.print(" ");
    Serial.println(byte4, HEX);

    value = byte4 | (byte3 << 8) | (byte2 << 16) | (byte1 << 24);
    //value = byte1 | (byte2 << 8) | (byte3 << 16) | (byte4 << 24)
  }
  Wire.endTransmission();  // End transmission to EPS
  //delay(100)

  return value;  // Return the 32-bit data read from EPS
}

\ Serial Monitor Output //

Received bytes: 1 0 0 1

Read Device ID: 0x1

Received bytes: 9 F2 D4 40

Read Device ID: 0xFFFFD440

Received bytes: 4B EE 44 0

Read Device ID: 0x4400

joaoclaudioeb commented 1 month ago

Hello, @MatthewStueber, how are you? I hope you're doing well! Based on your code, I am not sure if you are sending a data request in the correct format. In firmware/devices/obdh/obdh.c, you will notice that the EPS2 expects from the OBDH2 (or any other device trying to communicate with it via I²C) either a write request or a read request. In the case of a read request, as it seems to be the case here, it expects 2 bytes: the data to be read from its data structure and the CRC. Thus, the command you want to send in this case is 0x30 0x90, with 0x90 being the CRC. The code below is found at this link:

int obdh_decode(uint8_t *adr, uint32_t *val, uint8_t *cmd)
{
    int err = 0;

    uint8_t buf[I2C_RX_BUFFER_MAX_SIZE] = {0};
    uint16_t received_size = 0;

    if (i2c_slave_read(buf, &received_size) == 0)
    {
        if (obdh_check_crc(buf, received_size - 1U, buf[received_size - 1U]) == true)
        {
            switch(received_size)
            {
                case OBDH_COMMAND_WRITE_SIZE:
                    *adr = buf[0];
                    *val = ((uint32_t)buf[1] << 24) |
                           ((uint32_t)buf[2] << 16) |
                           ((uint32_t)buf[3] << 8)  |
                           ((uint32_t)buf[4] << 0);
                    *cmd = OBDH_COMMAND_WRITE;

                    break;
                case OBDH_COMMAND_READ_SIZE:
                    *adr = buf[0];
                    *val = 0;
                    *cmd = OBDH_COMMAND_READ;

                    break;
                default:
                    sys_log_print_event_from_module(SYS_LOG_ERROR, OBDH_MODULE_NAME, "Invalid command received (CMD)!");
                    sys_log_new_line();

                    err = -1;

                    break;
            }
        }
        else
        {
            sys_log_print_event_from_module(SYS_LOG_ERROR, OBDH_MODULE_NAME, "Invalid command received (CRC)!");
            sys_log_new_line();

            err = -1;
        }
    }
    else
    {
        err = -1;
    }

    return err;
}

Additionally, the EPS2 will respond with 6 bytes. In this specific case, it will respond 0x30 0x00 0x00 0xEE 0xE2 0x63, with 0x63 being the CRC of the message. Could you check your sending and receiving functions to confirm that the message being sent is correct and that the reception is in accordance with this format?

MatthewStueber commented 1 month ago

Hi @joaoclaudioeb, I am doing great! Thank you for your response, and I appreciate the detailed explanation regarding the I2C protocol. After reviewing your informative message I have corrected my Arduino code. The code now uses the correct format for sending and receiving functions, and the EPS 2.0 responds as specified. Below is the corrected code and Serial Monitor output.

/*
  This code is used with the SpaceLab EPS 2.0.
  The code will send a read command to the EPS.
  The command is 2 bytes: the first byte is the ID, followed by a 
  Cyclic Redundancy Check (CRC). This code sends the ID 
  of 48 which is 0x30 in Hex. The EPS then responds with 
  6 bytes of data. The response structure is as follows:
  it begins with the requested ID, followed by 2 padding bytes
  of 0x00. The last two bytes contain the requested data, for this
  code it is the Device ID (0xEEE2). The response concludes with the
  CRC byte verification.
*/

#include <Wire.h>

#define EPS_I2C_ADDRESS 0x36  // EPS's I2C address

void setup() {
  Wire.begin();  // Initialize I2C communication
  Serial.begin(9600);  // Initialize serial communication at 9600 baud
  Serial.println("");
  Serial.println("________START_________");
  // Writing the ID to the EPS
  uint32_t deviceID = 0;  // ID value you want to write
  uint8_t message_ID = 0x30;
  uint8_t message_CRC = 0x90;
  uint16_t value = 0;

  // 2 Runs
  for(int i=0; i<2; i++){
    Wire.beginTransmission(EPS_I2C_ADDRESS); // Begin I2C communication (EPS Address(0x36))
    // read request must be HEX(ID) + CRC (0x90)
    Wire.write(message_ID); // ID is 48 = 0x30 
    Wire.write(message_CRC); // CRC = 0x90
    Wire.endTransmission(); // End of request
    delay(100);

    // READ Responce should be 6 bytes (64 bits)
    Wire.requestFrom(EPS_I2C_ADDRESS, 6); // Request for responce of 6 bytes
    if(Wire.available() == 6){
      uint8_t byte1 = Wire.read(); 
      uint8_t byte2 = Wire.read();
      uint8_t byte3 = Wire.read();
      uint8_t byte4 = Wire.read();
      uint8_t byte5 = Wire.read();
      uint8_t byte6 = Wire.read();

      // Serial.println(byte1, HEX);
      // Serial.println(byte2, HEX);
      // Serial.println(byte3, HEX);
      // Serial.println(byte4, HEX);
      // Serial.println(byte5, HEX);
      // Serial.println(byte6, HEX);

      value = byte5 | (byte4 << 8);
      Serial.print("Return State: 0x");
      Serial.println(value, HEX);
    }
  }
  Serial.println("___________END___________");

}

void loop() {
  // Empty loop
}

____START____

Return State: 0xEEE2

Return State: 0xEEE2 ____END____

MatthewStueber commented 1 month ago

Hi @joaoclaudioeb I have been messing around with interfacing devices and came across the BeagleBone Black (BBB). I am trying to use the BBB to interface with the EPS 2.0. With the BBB I would like to read and write the commands the following commands to and from the EPS 2.0:

 /**
     *  EPS MCU related data.
     */
    uint32_t time_counter_ms;                   /**< Time counter in milliseconds. */
    uint8_t last_reset_cause;                   /**< EPS MCU last reset cause */
    uint16_t reset_counter;                     /**< EPS MCU reset counter */
    uint16_t eps_mcu_temp_kelvin;               /**< EPS MCU temperature in kelvin. */
    uint16_t eps_beacon_ma;                     /**< EPS circuitry and Beacon MCU current in mA. */
    uint16_t main_power_buss_mv;                /**< Main power buss voltage in mV. */
    uint8_t mppt_1_duty_cycle;                  /**< MPPT 1 duty cycle in %. */
    uint8_t mppt_2_duty_cycle;                  /**< MPPT 2 duty cycle in %. */
    uint8_t mppt_3_duty_cycle;                  /**< MPPT 3 duty cycle in %. */
    uint8_t mppt_1_mode;                        /**< MPPT 1 mode flag. */
    uint8_t mppt_2_mode;                        /**< MPPT 2 mode flag. */
    uint8_t mppt_3_mode;                        /**< MPPT 3 mode flag. */
    uint8_t heater1_mode;                       /**< Heater 1 mode flag. */
    uint8_t heater2_mode;                       /**< Heater 2 mode flag. */

I am powering the EPS 2.0 with a power supply set to 3.7V for reference and 7.4 V at 0.8A for power. The power supply is connected to the battery connector on the EPS 2.0 and is powering the board correctly. The I2C bus from the EPS 2.0 (H2-49 and H2-51 for SDA and SCL respectively) is connected to the pins on the BBB (P920 and P919 for SDA and SCL respectively). Upon integrating the BBB, I came across a few problems. I am able to write the 0x30 command ID and the 0x90 CRC to the EPS 2.0 and read the response with no problem, but upon changing the command ID from 0x30 to another ID such as 0x12 (ID 18 to measure main power bus voltage in mV) the output either does not change or is not good data. My code and output is displayed below. Is there a separate CRC for each ID? Is there a different command structure for command IDs that are not uint16_t? Should I be expecting 6 bytes return for each command? Could you please clarify the I2C protocol being used, as well as where that is defined in the documentation? I am getting confused on the format/ command structure of the data being sent to the device to read back a proper response.

____Code____

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdint.h>

#define EPS_I2C_ADDRESS 0x36 // EPS's I2C address
#define I2C_BUS "/dev/i2c-2"  // BBB I2C Bus

int open_i2c()
{
int path;
  if((path = open(I2C_BUS, O_RDWR)) < 0)
  {
    perror("Failed to connect to device");
    return -1;
  }
  if(ioctl(path, I2C_SLAVE, EPS_I2C_ADDRESS) < 0)
  {
    perror("Failed to connect to device");
    close(path);
    return -1;
  }
  return path;
}

int comm(int path, u_int8_t *data, size_t data_len, u_int8_t *res,
size_t res_len)
{

  if(write(path, data, data_len) != data_len)
  {
    perror("Failed to write");
    return -1;
  }
  usleep(40000); // Small delay to wait for response
  if(read(path, res, res_len) != res_len)
  {
    perror("Failed to read");
    return -1;
  }
  return 0;

}

void display(u_int8_t *res, size_t res_len)
{
  for(int i=0; i<res_len; i++)
  {
    printf(" ");
    printf("0x%X", res[i]);
  }
  printf("\n");
}

int main()
{
  int file1;
  int file2;
  int file3;
  u_int8_t ID_1 = 0x12;
  u_int8_t CRC_1 = 0x90;
  u_int8_t buf1[2];
  u_int8_t res1[200] = {0xFF};
  buf1[0] = ID_1;
  buf1[1] = CRC_1;

  file1 = open_i2c();
  if(comm(file1, buf1, 2, res1, 200) == -1){
    printf("Error writing or reading I2C connection.\n");
    return (-1);
    }
  display(res1, 200);
  close(file1);
  return 0;

}

output with ID = 0x12 and CRC = 0x90

debian@BeagleBone:~/EPS$ ./a.out
 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0xC2 0x1 0x0 0x8 0x0 0x0 0x0 0xF0 0x29 0xF 0x0
0x20 0x2C 0xF 0x0 0x5C 0x1 0x0 0x0 0x1 0x0 0x9 0xF2 0xD4 0x40 0xA3
0x4B 0xEE 0x44 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x2 0x0 0xA0 0x86 0x1
0x0 0x42 0x45 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x60 0x6 0x2 0x0 0x0
0x61 0x2 0x3 0xE6 0x2C 0x2 0x0 0xF 0x0 0x0 0x1 0x0 0x0 0x0 0x0 0xAC
0x2C 0xF 0x0 0x0 0x0 0x59 0x0 0x70 0x2C 0xF 0x0 0xB1 0x3 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x8B 0xFE 0xFE 0x3F 0xE3 0x9D 0xB7
0xFB 0xBD 0xDA 0xF6 0xFE 0xFB 0x22 0xDF 0x4C 0xD4 0x64 0xFB 0xBD 0xEA
0x7B 0xAD 0xEF 0xFF 0x9F 0xF7 0xA3 0x7A 0xD9 0x98 0xFD 0xAB 0xB9 0xBC
0xF1 0xD5 0x36 0xFD 0xF6 0x75 0xE6 0xFF 0x3A 0xCD 0x75 0x2C 0x80 0xF6
0xFA 0x2C 0xF 0x3F 0xD4 0xE8 0xDB 0x5E 0xFB 0xCF 0x5D 0x60 0x33
debian@BeagleBone:~/EPS$ ./a.out
 0x94 0x8D 0xFF 0xCA 0xBB 0xAB 0x3F 0xB7 0xD2 0xF4 0x7E 0xF9 0xAB 0x6D
0xEC 0xF5 0x72 0xAC 0xCF 0xAE 0x33 0x8C 0xF2 0xDC 0xB7 0xBE 0xFD 0xAF
0xB3 0xFF 0xE7 0x9B 0x95 0xAA 0xBF 0x6D 0xC7 0xBF 0x9D 0x4E 0x46 0xFC
0xD9 0x9D 0xAF 0xF5 0xFF 0xFF 0xB8 0x7F 0xC7 0x97 0xEE 0x7 0xFB 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0xC2 0x1 0x0 0x8 0x0 0x0 0x0 0xF0 0x29 0xF 0x0 0x20
0x2C 0xF 0x0 0x5C 0x1 0x0 0x0 0x1 0x0 0x9 0xF2 0xD4 0x40 0xA3 0x4B
0xEE 0x44 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x2 0x0 0xA0 0x86 0x1 0x0
0x42 0x45 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x60 0x6 0x2 0x0 0x0 0x61
0x2 0x3 0xE6 0x2C 0x2 0x0 0xF 0x0 0x0 0x1 0x0 0x0 0x0 0x0 0xAC 0x2C
0xF 0x0 0x0 0x0 0x59 0x0 0x70 0x2C 0xF 0x0 0xB8 0x3 0x0 0x0 0x0 0x0
0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x8B 0xFE 0xFE 0x3F 0xE3 0x9D 0xB7

upon switching the variable ID_1 to be equal to 0x30 (ID 48) instead of 0x12 (ID 18)

output with ID = 0x30 and CRC = 0x90

debian@BeagleBone:~/EPS$ ./a.out
 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0
debian@BeagleBone:~/EPS$ ./a.out
 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63
0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0 0xEE 0xE2 0x63 0x30 0x0 0x0
0xEE 0xE2 0x63 0x30 0x0

Please let me know if you have any suggestions for my code. Thank you for your time and effort.

joaoclaudioeb commented 1 month ago

Hello, @MatthewStueber, how are you? I hope you're doing well! We can think of cyclic redundancy check (CRC) as a method for detecting errors in data transmission. For example, if a device wants to request specific information from the EPS, it needs to send both the address of the data and a CRC. The CRC is a value calculated by the device based on the message being sent. The EPS performs its own CRC calculation and compares it with the received CRC. If they don’t match, it usually indicates an error during transmission.

In the example I provided earlier, 0x90 is the CRC calculated for a message containing 0x30. Each message has a unique CRC value. For reference, you can check the code in firmware/devices/obdh/obdh.c (in this link), where there is a function for CRC calculation that you can use as a basis. I hope this helps clarify things!

MatthewStueber commented 1 month ago

Hi @joaoclaudioeb, I'm glad to hear you're doing well. I'm doing well also, and I appreciate your continued support. I’ve reviewed the documentation and implemented a function to calculate the CRC as shown in the code below. The code runs successfully, and I consistently receive data from the EPS. I understand that the EPS's response should always be 6 bytes: the first byte is the ID, followed by 4 bytes of data (depending on the ID type: uint8, uint16, or uint32), and ending with the CRC. However, I'm struggling to make sense of the data I'm receiving, particularly when querying specific registers.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <string.h>
#include <stdint.h>

#define EPS_I2C_ADDRESS 0x36 // EPS's I2C address
#define I2C_BUS "/dev/i2c-2"  // BBB I2C Bus
#define OBDH_CRC8_INITIAL_VALUE 0    /**< CRC8-CCITT initial value. */
#define OBDH_CRC8_POLYNOMIAL 0x07    /**< CRC8-CCITT polynomial. */

// Function to calculate the CRC-8
uint8_t calculate_crc8(uint8_t *data, uint8_t len) {
    uint8_t crc = OBDH_CRC8_INITIAL_VALUE;

    for (uint8_t i = 0; i < len; i++) {
        crc ^= data[i];

        for (uint8_t j = 0; j < 8; j++) {
            crc = (crc << 1) ^ ((crc & 0x80) ? OBDH_CRC8_POLYNOMIAL : 0);
        }
    }

    return crc & 0xFF; // Ensure CRC is in the range of 8-bit value
}

int open_i2c() {
    int path;
    if ((path = open(I2C_BUS, O_RDWR)) < 0) {
        perror("Failed to connect to device");
        return -1;
    }
    if (ioctl(path, I2C_SLAVE, EPS_I2C_ADDRESS) < 0) {
        perror("Failed to connect to device");
        close(path);
        return -1;
    }
    return path;
}

int comm(int path, uint8_t *data, size_t data_len, uint8_t *res,
size_t res_len) {
    if (write(path, data, data_len) != data_len) {
        perror("Failed to write");
        return -1;
    }
    usleep(40000); // Small delay to wait for response
    if (read(path, res, res_len) != res_len) {
        perror("Failed to read");
        return -1;
    }
    return 0;
}

void display(uint8_t *res, size_t res_len) {
    for (int i = 0; i < res_len; i++) {
        printf(" 0x%X", res[i]);
    }
    printf("\n");
}

int main() {
    int file1;
    uint8_t ID_1 = 0x30;
    uint8_t buf1[2];
    uint8_t res1[6] = {0xFF};

    // Prepare the data
    buf1[0] = ID_1;
    buf1[1] = calculate_crc8(&ID_1, 1); // Calculate CRC for the data

    // Open I2C communication
    file1 = open_i2c();
    if (file1 < 0) {
        return -1;
    }

    // Communicate over I2C
    if (comm(file1, buf1, 2, res1, 6) == -1) {
        printf("Error writing or reading I2C connection.\n");
        close(file1);
        return -1;
    }

    // Display the received response
    display(res1, 6);

    // Close the I2C connection
    close(file1);
    return 0;
}

When the code is run with the ID_1 = 0x30 the EPS returns what I expect, 0x30 0x0 0x0 0xEE 0xE2 0x63 as you can see with the exact output below.

debian@BeagleBone:~/EPS$ ./a.out
 0x30 0x0 0x0 0xEE 0xE2 0x63
debian@BeagleBone:~/EPS$ ./a.out
 0x30 0x0 0x0 0xEE 0xE2 0x63

When I request the temperature of the MCU with ID 0x01, the returned data is all zeros, which doesn’t seem right.

debian@BeagleBone:~/EPS$ ./a.out
 0x1 0x0 0x0 0x0 0x0 0x62
debian@BeagleBone:~/EPS$ ./a.out
 0x1 0x0 0x0 0x0 0x0 0x62

For the request of EPS circuitry current I am expecting a return of ~64mA.

debian@BeagleBone:~/EPS$ ./a.out
 0x2 0x0 0x0 0x0 0x22 0x2A
debian@BeagleBone:~/EPS$ ./a.out
 0x2 0x0 0x0 0x0 0x22 0x2A

For the request of the main power bus voltage I am expecting a return of ~8V.

debian@BeagleBone:~/EPS$ ./a.out
 0x12 0x0 0x0 0x14 0x6C 0xF6
debian@BeagleBone:~/EPS$ ./a.out
 0x12 0x0 0x0 0x14 0x6C 0xF6

For the request of the hardware and firmware version I am expecting a non-zero return.

debian@BeagleBone:~/EPS$ ./a.out
 0x29 0x0 0x0 0x0 0x0 0x1F
debian@BeagleBone:~/EPS$ ./a.out
 0x29 0x0 0x0 0x0 0x0 0x1F
debian@BeagleBone:~/EPS$ ./a.out
 0x2A 0x0 0x0 0x0 0x0 0xB9
debian@BeagleBone:~/EPS$ ./a.out
 0x2A 0x0 0x0 0x0 0x0 0xB9

The problem arises when I query registers other than the device ID; the returned data often doesn’t align with expectations. The data for MCU temperature, EPS circuitry current, and main power bus voltage, among others, seem off. I’ve verified my power supply and checked the connections, but the responses still don’t make sense. Could there be any additional processing required to correctly interpret the data? Or are there any adjustments to the code that you’d recommend? Thank you for your time and guidance. I look forward to your insights.

ramonborba commented 1 month ago

Hi @MatthewStueber, thanks for the update! Your code seems to be correct. We are running some tests of our own to try and replicate your problem and give you a better solution. In the mean time, could you please check if the resistor J_V4 (see the image for reference) is soldered on your EPS 2.0 board? It may explain some of the problems you are encountering.

image

MatthewStueber commented 1 month ago

Hi @ramonborba,

Thank you for your continued support. I have reviewed PCB and confirmed that the J_V4 resistor connected. I measured the resistor and got a reading of approximately 0.2 Ohms, which aligns with what I expected. I’ve uploaded two images showing the J_V4 connection for your reference. Please let me know if there are any additional steps I should take or further developments. Thanks again for all your help! J_V4(1) J_V4(2)

joaoclaudioeb commented 1 month ago

Hi @MatthewStueber, thanks for the update!

If J_V4 is soldered onto the PCB, it's likely that the voltage-related measurements are incorrect, as this bypasses the EPS reference circuit used for analog readings. In the EPS, measurements are typically based on a 2.5V reference. When bypassed, the EPS still calculates as if it were 2.5V, but it's actually 3.3V. For this specific problem, you can try correcting it in the EPS by reprogramming in firmware/drivers/adc/adc.h:

#define ADC_MODULE_NAME     "ADC"
#define ADC_VREF_V          (2.5)       /**< ADC reference voltage in Volts. */
#define ADC_VREF_MV         (2500UL)    /**< ADC reference voltage in millivolts. */
#define ADC_RANGE           (4095UL)    /**< ADC resolution (12-bits) */

Furthermore, we're working on a branch with some minor adjustments to address other related readings and believe we've replicated and fixed the issue. This branch should be available around August 12-13. Once it's out, I'll provide a detailed explanation of what happened.

MatthewStueber commented 1 month ago

Hi @joaoclaudioeb,

Thank you for your guidance. I’ve updated the firmware by setting ADC_VREF_V to 3.3 and ADC_VREF_MV to 3300UL, as you suggested. I verified the reference voltage on the EPS PCB and found it to be approximately 2.678V, which aligns well with the 3.3V setting.

The ADC returns are now more accurate, but there are still some discrepancies compared to what I was expecting. Below are the latest outputs:

debian@BeagleBone:~/EPS$ ./a.out
 0x0 0x0 0x0 0x8 0xE6 0x14
 0x1 0x0 0x0 0x0 0x0 0x62
 0x2 0x0 0x0 0x0 0x2D 0x7
 0x3 0x0 0x0 0x0 0x2 0xA8
 0x4 0x0 0x0 0x0 0x0 0x8F
 0xE 0x0 0x0 0x0 0x32 0xCC
 0xF 0x0 0x0 0x0 0x32 0xAE
 0x10 0x0 0x0 0x0 0x32 0xAC
 0x12 0x0 0x0 0x1D 0x9 0x77
 0x29 0x0 0x0 0x0 0x0 0x1F
 0x2A 0x0 0x0 0x0 0x0 0xB9
 0x2B 0x0 0x0 0x0 0x0 0xDB
 0x2C 0x0 0x0 0x0 0x0 0xF2
 0x2D 0x0 0x0 0x0 0x0 0x90
 0x30 0x0 0x0 0xEE 0xE2 0x63
debian@BeagleBone:~/EPS$ ./a.out
 0x0 0x0 0x1 0xDD 0xA6 0x43
 0x1 0x0 0x0 0x0 0x0 0x62
 0x2 0x0 0x0 0x0 0x2D 0x7
 0x3 0x0 0x0 0x0 0x2 0xA8
 0x4 0x0 0x0 0x0 0x0 0x8F
 0xE 0x0 0x0 0x0 0x32 0xCC
 0xF 0x0 0x0 0x0 0x32 0xAE
 0x10 0x0 0x0 0x0 0x32 0xAC
 0x12 0x0 0x0 0x1D 0xC 0x6C
 0x29 0x0 0x0 0x0 0x0 0x1F
 0x2A 0x0 0x0 0x0 0x0 0xB9
 0x2B 0x0 0x0 0x0 0x0 0xDB
 0x2C 0x0 0x0 0x0 0x0 0xF2
 0x2D 0x0 0x0 0x0 0x0 0x90
 0x30 0x0 0x0 0xEE 0xE2 0x63
debian@BeagleBone:~/EPS$

As shown, the current and voltage readings are still slightly off, and the hardware/firmware version values are not coming through correctly. Additionally, the temperature reading from the microcontroller is still returning zeros. Could you advise on other adjustments that might be needed? Should I make the rest of the the changes in the fix/eps-data-structure/#197? Also, I am curious about the counter command: does it involve the watchdog timer, or is it controlled differently? I appreciate your continued support and look forward to your insights. Thank you!

joaoclaudioeb commented 1 month ago

Hi, @MatthewStueber, could you clarify a few things?

1) Are you using the EPS to power something? The data from ID 0x02 (EPS2_PARAM_ID_EPS_CURRENT) is related to the current from the 3.3V converter in the EPS, which usually powers some internal EPS circuits and the TTC. So, you won't see the 64mA from the power supply in this data; you'll see something slightly lower. The 64mA will appear in the data from ID 0x1B (EPS2_PARAM_ID_BAT_CURRENT), which is related to the current drawn from the battery. Some circuits are directly connected to the VBUS (e.g., converters, battery monitor), while others are connected to the output of the 3.3V converter. This explains the difference in readings. However, converting the current you read in HEX, it seems something else might be drawing from the 3.3V bus. Can you confirm this?

2) The VBUS voltage reading from ID 0x18 (EPS2_PARAM_ID_SP_MZ_CURRENT) seems relatively accurate, assuming you're powering the EPS with 7.4V and 3.7V (for reference). Can you confirm this?

Furthermore, no need to touch the fix/eps-data-structure/#197 branch; I'll be making corrections today and tomorrow. The MCU temperature and possibly some other measurements aren't being updated in the data structure, which is why you're seeing zero values. This is a simple fix. However, if you're seeing strange values in the updated readings, it might be because negative numbers are being sent as uint16 instead of int16. I'll correct these details and check if there are any other odd values being sent.

Thank you for your patience and interest in our platform! If you'd like, feel free to share more about your project and goals with us — we'd love to hear about it! I'll let you know as soon as I make the changes in the new branch. Talk soon!

Note: some data will still show as 0 in the current setup, such as IDs 0x2B (EPS2_PARAM_ID_MPPT_1_MODE), 0x2C (EPS2_PARAM_ID_MPPT_2_MODE), and 0x2D (EPS2_PARAM_ID_MPPT_3_MODE). Take a look at eps2/firmware/app/structs/eps2_data.h.

Note²: regarding your other question, check out the FreeRTOS documentation—it might be helpful! We use FreeRTOS's xTaskGetTickCount() for that counting.

MatthewStueber commented 1 month ago

Hi @joaoclaudioeb,

Thank you for your prompt clarification. I’ve been testing with the EPS without any load attached, powering it through the battery connector with the 3.7V and 7.4V sources as outlined in the documentation. I’ve confirmed the register returns with a BeagleBone Black connected via the 3.3V I2C data lines. I appreciate the reasoning behind changing the uint16 to int16 and look forward to seeing the updates.

I’m currently working with my team on designing a 3U CubeSat, using the BeagleBone Black as the OBC and the SpaceLab EPS 2.0 to better understand typical CubeSat electrical power subsystem designs. While the EPS is fabricated, we don’t have the BAT4C battery module. Our end goal is to establish a complete test setup with the SpaceLab EPS 2.0, a solar array emulator, and a spacecraft load emulator.

Regarding the solar array emulator, I’ve been trying to power the EPS through three of the six solar array connectors. Currently, with the power supply set to 7V and 800mA connected to the -Y, -X, and -Z connectors, only the VBUS_LED turns on. Additionally, the 5V_Radio_0_LED and 6V_Radio_1_LED flicker, and a DMM connected to the 3.3V output is reading 0V. Do you think the BAT4C is necessary for powering the EPS through these connectors? Any suggestions on how to fully power the EPS via the solar array inputs would be greatly appreciated.

Thanks again for your help!

joaoclaudioeb commented 1 month ago

Good evening, @MatthewStueber. Thanks for updating us on your test setup. We have a few suggestions for you to consider:

1) For now, avoid using the EPS without a connected battery. You can use a power supply to power the board with 7.4V on VBAT+, GND on VBAT-, and 3.7V on V_CM. You can use the P5 connector for this.

2) There are various ways to emulate solar panels, but the simplest is described in [1]. In this case, you're only simulating the MPP point without much complexity. We use this method for simpler experiments.

3) You can try using the dev_firmware branch while we make the more significant updates on the new branch. @ramonborba and I have created some other branches that were needed, but dev_firmware is the most advanced.

4) Another thing you can do is enable the EPS2 debug prints and capture the logs. You'll see that the readings in the logs are correct since they consider the appropriate variable types. In config.h, there is a define called CONFIG_TASK_READ_SENSORS_DEBUG_ENABLED that you can set to enable this. Then, check the logs and compare them with the values obtained by the "onboard computer," which could be the Arduino or BBB.

Additionally, I think the initial readings you should focus on evaluating are the following IDs:

You can also verify the variable types by reviewing the calls to sys_log_print_uint() or sys_log_print_int() to correctly convert the readings on the Arduino or BBB. Some variables, regardless of being signed or not, will continue to be stored as uints. So, it’s up to the ground segment to make the necessary conversions. In your case, you can read and convert sequentially. The relevant file is eps2/firmware/app/tasks/read_sensors.c. It's worth noting that we will update the EPS2 documentation's parameter table to reflect the correct types for each variable, but for now you could use this file as reference, since the prints accurately reflect the correct variable types.

Note: maybe Ramon and I will do some updates in the next days.

[1] Chalh, A., Motahhir, S., El Hammoumi, A. et al. Study of a Low-Cost PV Emulator for Testing MPPT Algorithm Under Fast Irradiation and Temperature Change. Technol Econ Smart Grids Sustain Energy 3, 11 (2018). https://doi.org/10.1007/s40866-018-0047-8