2
\$\begingroup\$

I have a I2C setup which consists of a Raspberry Pi 3 Model B as a master. The Pi is connected to a PCB motherboard with several I2C devices on it. Communication works flawlessly with all I2C devices except for two CubeSense Sun Sensors.

When these sensors are powered on, I can successfully send a command and read the correct data from the sensor. This is shown in the following picture:

enter image description here

However, for all subsequent commands, the master reads 0x00's from the sensor. This is shown in the following picture:

enter image description here

The device requires a power cycle to properly respond to a command but will always respond with 0x00 for the second command and all subsequent commands. Everything in the communication seems to be fine, it's just instead of meaningful bytes it returns 0x00.

What could be the problem here?

P.S. this is my first post on here so if there's any information not included in my post that could/should have been included to help, please let me know.

Edit:

  1. Here is a link to the datasheet of the CubeSense Sun sensors: https://drive.google.com/file/d/1qolckhwerZmrEwz7TgLOyAlw0ZPxC3ma/view?usp=share_link

  2. I cannot say for sure. This project is for a university research team, and I took over responsibility for this system recently. I have documentation that communication with this sensor had worked in the past, but they may have only issued that first command. I've compared their code with what I have now and see no relevant changes.

  3. I cannot make the hardware changes necessary to connect just one sensor to the RPi. I tried switching from running the team's flight code to a script that performs a R/W on one sensor with all other I2C devices removed from the board. The write was an 0x81 byte for a Read Serial Number command. The R/W was repeated every 1 second. This was the output:

0x43 0x36 0x2 0x2 0x2 0x3

0xb 0x0 0x6c 0x2 0x1 0x0

0x0 0x0 0x0 0x0 0x0 0x0

0x1 0x1 0x1 0x32 0x0 0x2

0x0 0x0 0x0 0x8 0x0 0x0

0x0 0x0 0x0 0x0 0x0 0x0

0x0 0x0 0x0 0x0 0x0 0x0

...and 0x0's from them on. The logic analyzer only shows 0x00's for READs after the first one.

  1. I then performed the R/W to run based on user input. Here are oscilloscope readings of the first R/W and second R/W:

First R/W

Second R/W

  1. The sensors connect to, and are powered through, the motherboard PCB. The RPi pins connect to the motherboard as well.

These are the driver ICs used to turn the sensors on/off:

enter image description here

This is the driver IC datasheet

SS-PWR is a GPIO pin. The Sun sensors use 3.3V and 5V supplies.

The I2C pins from the RPi connect to these logic level-shifter ICs:

enter image description here

This is the level-shifter IC datasheet

SDA/SCL come from the RPi. The b-side SDA/SCL lines go directly to the Sun sensor's header.

For code, we have a UI file to run commands through:

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>

#include "hardware_interfaces/sun/sunSensor.hpp"
#include "state_machine/constants.hpp"

#define SIZE_OF(a) (sizeof(a) / sizeof(*a))

#define QUIT                  0
#define MENU_TURN_ON          (QUIT + 1)
#define MENU_TURN_OFF         (MENU_TURN_ON + 1)
#define READ_SERIAL           (MENU_TURN_OFF + 1)

static char* actions[] = {"Quit", "Turn on both Sun Sensors", "Turn off both Sun Sensors", "Serial Number"};

int main(void) {
    int input          = -1; // User input
    int pin_on = 1;
    int pin_off = 0; 
    uint8_t buffer[6] = {0};

    setupSun();

    while (true) {
        printf("\nMenu:\n");
        for (int i = 0; i < (signed)SIZE_OF(actions); i++) {
            printf(" %d) ", i);
            if (i > QUIT && i < MENU_SET_CAM_SETTINGS)
                printf("Read ");
            printf("%s\n", actions[i]);
        }
        printf("=> ");
        scanf("%d", &input);
        printf("\n");

        if (input < 0 || input > (signed)SIZE_OF(actions) - 1) {
            printf("please input a value between 0 and 3.\n");
            continue;
        }

        switch (input) {
            case QUIT: return 0; break;

            case MENU_TURN_ON:
                if(digitalRead(PINS::SS_PWR) == pin_on) {
                    printf("Both Sun sensors are already turned on");
                    break;
                }
                pinMode(PINS::SS_PWR, OUTPUT);
                digitalWrite(PINS::SS_PWR, HIGH);
                if (digitalRead(PINS::SS_PWR) == pin_on) {
                    printf("Both Sun sensors turned on");
                } else {
                    printf("Could not turn on either Sun sensor");
                }
                break;

            case MENU_TURN_OFF:
                if (digitalRead(PINS::SS_PWR) == pin_off) {
                    printf("Both Sun sensors already turned off");
                    break; 
                }
                digitalWrite(PINS::SS_PWR, LOW);
                if (digitalRead(PINS::SS_PWR) == pin_off) {
                    printf("Both Sun sensors turned off");
                } else {
                    printf("Could not turn off either Sun sensor");
                }
                break;
        
            case READ_SERIAL:
                printf("Reading serial number 1: ");
                getSerial(buffer, 1);
                for (int i = 0; i < 6; i++) printf("%c", buffer[i]);
                printf("\n");

                printf("Reading serial number 2: ");
                getSerial(buffer, 2);
                for (int i = 0; i < 6; i++) printf("%c", buffer[i]);
                printf("\n");
                break;

            default: printf("Switch case ERROR!\n");
        }
        input = 0;
    }
    return 0;
}

That UI file includes this .cpp file where the functions are:

#include "sunSensor.hpp"

static int sun1_file;
static int sun2_file;

void setupSun() {
    sun1_file = wiringPiI2CSetup(SUN1_ADDRESS);
    if (sun1_file < 0) {
        printf("Failed to open Sunsensor 1 device.\n");
        exit(1);
    }
    sun2_file = wiringPiI2CSetup(SUN2_ADDRESS);
    if (sun2_file < 0) {
        printf("Failed to open Sunsensor 2 device.\n");
        exit(1);
    }
}

void getSerial(uint8_t* buffer, int select) {
    uint8_t command = SERIAL;
    int device;

    switch (select) {
        case 1: device = sun1_file; break;
        case 2: device = sun2_file; break;
        default: printf("Incorrect device passed into getSerial()\n"); return;
    }

    if (write(device, &command, 1) != 1) {
        printf("Failed to send command.\n");
        return;
    }

    if (read(device, buffer, 6) != 6) {
        printf("Failed to read all serial data.\n");
    }
}
}

and this .hpp file for defined values:

#ifndef SRC_SUNSENSOR_H
#define SRC_SUNSENSOR_H

#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>

// Commands
#define SUN_RESET 0x00

#define SERIAL     0x81
#define SERIAL_LEN 6

// I2C Addresses
#define SUN1_ADDRESS 0x10
#define SUN2_ADDRESS 0x11

static const char SUN_SERIAL_NUM0[] = "CS1601";
static const char SUN_SERIAL_NUM1[] = "CS1604";

/**
 * @brief       This function is used to initialize the connection to the sun
 *              sensors.
 */
void setupSun();

/**
 * @brief       This function is used to get the serial number of a specific sun
 *              sensor.
 * @param       buffer      The byte buffer to hold the data.
 * @param       select      The number of the sensor.
 */
void getSerial(uint8_t* buffer, int select);


#endif

If any further information is required, please let me know.

\$\endgroup\$
6
  • \$\begingroup\$ Joshkoz16 - Hi, (a) Please link to the user manual for those sensors, showing how they should be accessed via I2C (cmd sequences etc). (b) Have you ever been successful sending multiple commands via the RPi or any other I2C master (controller)? (c) You mentioned having multiple sensors. Can you temporarily simplify your setup to just one of those sensors? Any change to behaviour? (d) Have you looked at the I2C bus signal quality using an oscilloscope? (e) You said: "When these sensors are powered on" suggesting independent power control, which is unusual. Can you add the schematic? Thanks \$\endgroup\$
    – SamGibson
    Oct 21, 2023 at 16:53
  • \$\begingroup\$ There is no schematics or code, and only a logic analyzer trace that can't be zoomed in. Oscilloscope traces could show a problem in analog domain or signal integrity issues. \$\endgroup\$
    – Justme
    Oct 21, 2023 at 17:27
  • \$\begingroup\$ @SamGibson I have edited my post to answer your questions/requests as best I can. The amount of "tinkering" that I can do to this setup is limited as it's for a university research team and not my own personal project. \$\endgroup\$
    – Joshkoz16
    Oct 23, 2023 at 0:45
  • \$\begingroup\$ @Justme I have added code oscilloscope readings of successful and unsuccessful reads. The code is not mine to share so I've only shown what's relevant to the command used in testing, but failed second READs are consistent across all commands. If further oscilloscope readings or more zoomed in ones are needed, please let me know. \$\endgroup\$
    – Joshkoz16
    Oct 23, 2023 at 0:46
  • \$\begingroup\$ The voltages on both channels in those oscillograms seem to be decaying over the length of the communication. Not sure if that's related to the issue, but it's something odd, at least. You may have inadequate decoupling capacitors on something. \$\endgroup\$
    – Hearth
    Oct 23, 2023 at 2:28

1 Answer 1

0
\$\begingroup\$

There are likely hardware design errors.

The errors can even mask each other out.

First of all the power switching to the sensors may not work due to having simply capacitors connected to QOD and CT pins. It is possible that they are not turned on in the software too.

The second problem is that even if power switching works, it does not matter if the power is on or off, if the I2C pull-ups are on the always on supply. It may leak current to the sensor and pull the sensor supply up and charge the bypass caps. So evem if power supplies are off, the sensor is powered and you can communicate with it until it sensor power drops too low due to the bus wires being low during communictions so on average they leak less power to the sensor to keep voltage high enough.

There may be other errors in hardware and/or in software but you need to be sure what works and what doesn't.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.