IlliniHyperloopComputing/Pod

[ADC] Pick external ADC, and develop communication

Closed this issue · 9 comments

rgw3d commented

Once we have a new ADC, we will need to communicate with it over SPI or I2C (likely). How do we do this communication reliably?

rgw3d commented

For i2c something like this could be implemented in C++ https://raspberry-projects.com/pi/programming-in-c/i2c/using-the-i2c-interface

It may be worthwhile to clone ADCManager and make two versions--ExternalADCManager and BuiltInADCManager. If they are accessing two different components, they should be on separate refresh loops.

rgw3d commented

We are using the ADS1115: https://www.adafruit.com/product/1085

On the BBB, use command i2cdetect -y -r 2 to look for i2c devices on /dev/i2c-2

From Adafruit:
I2C Addressing
The ADS11x5 chips have a base 7-bit I2C address of 0x48 (1001000) and a clever addressing scheme that allows four different addresses using just one address pin (named ADR for ADdRess). To program the address, connect the address pin as follows:

    0x48 (1001000) ADR -> GND
    0x49 (1001001) ADR -> VDD
    0x4A (1001010) ADR -> SDA
    0x4B (1001011) ADR -> SCL
rgw3d commented

More debug information: kelly/node-i2c#27

rgw3d commented

Now that the low-level structure is developed on the branch pod4-i2c, we can use the interface!

Our goal: Read all 4 of the analog ports from each of ADCs that we have connected over i2c. Since each read takes about 3 milliseconds, we are only going to read one port per refresh().

We are going to be doing something very similar to what is already implemented in TMPManager.hpp. The TMPData struct is very similar. The way that the idx variable within the refresh() loop is very similar to what we want to accomplish

First thing to do is to modify the I2Cdata structure, such that we can store the data we read in into it. It is defined at the top of I2CManager.hpp, and only has a dummy variable. Since we know that all of our data will be int16_ts, (since this is a 16-bit adc), then we can simply create an array within the struct. Something like int16_t data[8] would work (maybe with a more descriptive name). Once this is implemented, then we can try to take on the refresh() loop.

For the refresh loop we want to read one value at a time, while still traversing all of the different ports, while keeping code size small. Look at how TMPManager.cpp does it. Look at line 27 to see that we open a file based on a value idx, which you can see that on line 50 we increment (and set back to 0 once we have covered all the values, that's what the modulo % operator does). The idx value is an index into an array which you can find defined in TMPManager.hpp.
You can ignore most of what TMPManager.cpp actually does, but the key part is what i listed above -- an index is used to select a variable from an array, which is used to selectively read in sensor data.

For the I2CManager.cpp refresh() loop, I'd approach it this way:
Identify what stays the same every loop. (hint, it is i2c_fd)
Identify what can change after a loop (hint, it is the 0x48 vs 0x49 nodeID, and the ANC0 vs ANC1... vs ANC3)

So, in a way similar to how TMPManager.cpp accomplishes it, we want to do:

int16_t value = 0;
if(!set_i2c_addr(i2c_fd,     /* ACCESS NODEID BASED ON INDEX */ 0x48             )){
     return false;
}
if(single_shot(i2c_fd, /* ACCESS PORT BASED ON INDEX */ ANC0, value)){
    print(LogLevel::LOG_INFO, "i2c val1: %d, in micros: %lu\n", value, b-a);
}

//STORE VALUE BASED ON INDEX
//INCREMENT INDEX

What values are being used through the external ADC? Just the accelerometers?

rgw3d commented

Internal ADCs handle Accelerometers
External ADCs handle temperature