Last active
February 23, 2023 10:40
-
-
Save el-hult/62ff50bc3f9aabf5378704ba75dc9f98 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This code reads the BNO055 fast using the RPi linux kernel support for I2C. | |
for running this file as a program, compile and run! | |
gcc -o faster faster.c -O3 && ./faster | |
You can also compile as a shared lib, and then call it from python. | |
gcc -Wall -shared -o faster.so faster.c && python faster.py | |
Page number and table number references are to the BNO055 data sheet. | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <time.h> | |
#include <sys/ioctl.h> | |
#include <linux/i2c-dev.h> | |
#define BNO055_I2C_ADDR 0x29 | |
const int REG_PAGE0_OPR_MODE = 0x3d; // See table 3-5 | |
const int REG_PAGE0_UNIT_SEL = 0x3b; | |
const int REG_PAGE1_ACC_Config = 0x08; | |
const int REG_PAGE_ID = 0x07; // same on page 1 and page 0 | |
const int REG_PAGE1_ACC_DATA_X_LSB = 0x08; | |
const int CONFIGMODE = 0; | |
const int ACCONLY = 1; | |
const int ACC_RANGE_2G = 0b00000000; // see table 3-8 | |
const int ACC_RANGE_16G = 0b00000011; // see table 3-8 | |
const int ACC_BW_7_81Hz = 0b00000000; // see table 3-8 | |
const int ACC_BW_1000Hz = 0b00011100; // see table 3-8 | |
const int ACC_MODE_NORMAL = 0b00000000; // power saving mode. see table 3-8 | |
const int my_accelerator_config = ACC_MODE_NORMAL | ACC_BW_1000Hz | ACC_RANGE_16G; | |
const int UNIT_CELSIUS = 0b00000000; // see table 3-11 | |
const int UNIT_RADIANS = 0b00000100; // see table 3-11 | |
const int UNIT_RADIANS_PER_SECOND = 0b00000010; // see table 3-11 | |
const int UNIT_METER_PER_SECOND_SQUARED = 0b00000000; // see table 3-11 | |
const int UNIT_MILLI_G = 0b00000001; // see table 3-11 | |
const int my_units = UNIT_CELSIUS | UNIT_RADIANS | UNIT_RADIANS_PER_SECOND | UNIT_METER_PER_SECOND_SQUARED; | |
// see https://stackoverflow.com/a/64539170/4050510 | |
int64_t millis() | |
{ | |
struct timespec now; | |
timespec_get(&now, TIME_UTC); | |
return ((int64_t)now.tv_sec) * 1000 + ((int64_t)now.tv_nsec) / 1000000; | |
} | |
int selectDevice(int fd, int addr, char *name) | |
{ | |
int s; | |
char str[128]; | |
s = ioctl(fd, I2C_SLAVE, addr); | |
if (s == -1) | |
{ | |
sprintf(str, "selectDevice for %s", name); | |
perror(str); | |
} | |
return s; | |
} | |
void writeToDevice(int fd, int reg, int val) | |
{ | |
int s; | |
char buf[2]; | |
buf[0] = reg; | |
buf[1] = val; | |
s = write(fd, buf, 2); | |
if (s == -1) | |
{ | |
perror("writeToDevice"); | |
} | |
else if (s != 2) | |
{ | |
fprintf(stderr, "short write to device\n"); | |
} | |
} | |
void saveToFile(void *data, int n_bytes) | |
{ | |
FILE *outfile; | |
// open file for writing | |
outfile = fopen("acc_data.bin", "w"); | |
if (outfile == NULL) | |
{ | |
fprintf(stderr, "\nError opened file\n"); | |
exit(1); | |
} | |
// write struct to file | |
size_t w = fwrite(data, n_bytes, 1, outfile); | |
if (w != 0) | |
printf("contents to file written successfully !\n"); | |
else | |
printf("error writing file !\n"); | |
// close file | |
fclose(outfile); | |
} | |
int setup() { | |
int fd; | |
char fname[] = "/dev/i2c-1"; // use I2C device 1 on all 'modern' RPis | |
if ((fd = open(fname, O_RDWR)) < 0) | |
{ | |
// Open port for reading and writing | |
fprintf(stderr, "Failed to open i2c bus %s", fname); | |
exit(1); | |
} | |
/* initialise BNO055 */ | |
selectDevice(fd, BNO055_I2C_ADDR, "BNO055"); | |
writeToDevice(fd, REG_PAGE_ID, 0); | |
writeToDevice(fd, REG_PAGE0_OPR_MODE, CONFIGMODE); | |
usleep(7000); // enter CONFIGMODE. also see table 3-6 | |
writeToDevice(fd, REG_PAGE0_UNIT_SEL, my_units); | |
writeToDevice(fd, REG_PAGE_ID, 1); | |
writeToDevice(fd, REG_PAGE1_ACC_Config, my_accelerator_config); // update accelerometer config on page 1 | |
writeToDevice(fd, REG_PAGE_ID, 0); | |
writeToDevice(fd, REG_PAGE0_OPR_MODE, ACCONLY); | |
usleep(19000); // exiting configmode see table 3-6 | |
return fd; | |
} | |
// see page 93 for details on reading multi bytes via i2c | |
void readAccData(int fd,int N, float out_arr[]) { | |
unsigned char buf[6]; | |
buf[0] = REG_PAGE1_ACC_DATA_X_LSB; // prepare the registry of acc data for reading | |
if ((write(fd, buf, 1)) != 1) | |
{ | |
fprintf(stderr, "Error writing to BNO055\n"); | |
} | |
for (int k = 0; k < N; k++) | |
{ | |
if (read(fd, buf, 6) != 6) | |
{ | |
fprintf(stderr, "Error reading from BNO055\n"); | |
} | |
else | |
{ | |
signed short int x = buf[1] << 8 | buf[0]; | |
signed short int y = buf[3] << 8 | buf[2]; | |
signed short int z = buf[5] << 8 | buf[4]; | |
out_arr[k * 3] = (float)x; | |
out_arr[k * 3 + 1] = (float)y; | |
out_arr[k * 3 + 2] = (float)z; | |
} | |
} | |
} | |
int main(int argc, char **argv) | |
{ | |
printf("Opening the I2C device, and configuring it\n"); | |
int fd = setup(); | |
const int N = 1000; | |
clock_t tic = millis(); | |
printf("Starts reading\n"); | |
float out_arr[3*N]; | |
readAccData(fd,N,out_arr); | |
printf("Done!\n"); | |
clock_t toc = millis(); | |
double elapsed_secs = (toc - tic) / 1000.0; | |
double frequency = (float)N / elapsed_secs; | |
printf("Collected %d samples in %f seconds, i.e. %f Hz\n", N, elapsed_secs, frequency); | |
printf("Dumping to file!\n"); | |
saveToFile(out_arr, sizeof(out_arr)); | |
printf("Done.\n"); | |
/* | |
can be read with | |
import numpy as np | |
np.fromfile('acc_data.bin','float32').reshape(-1,3) | |
*/ | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import timeit | |
import numpy as np | |
import ctypes | |
N=1000 | |
def main1(fd,lib): | |
"""read as fast as possible with the lib""" | |
out_arr = (ctypes.c_float * (3*N))() | |
lib.readAccData(fd,N,out_arr) | |
data = np.ctypeslib.as_array(out_arr) | |
def setup1(): | |
lib = ctypes.cdll.LoadLibrary('./faster.so') | |
lib.setup.restype = ctypes.c_int | |
fd = lib.setup() | |
return fd, lib | |
if __name__ == "__main__": | |
t1 = timeit.Timer( | |
stmt='main1(fd,lib)', | |
setup="from __main__ import main1, setup1; fd,lib=setup1();") | |
n,T = t1.autorange() | |
print(f"took {T:.2f} seconds to run main1 {n:d} times ({N*n/T:.1f} Hz)") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import timeit | |
import numpy as np | |
N=1000 | |
def main1(sensor): | |
"""read as fast as possible with the lib""" | |
data = np.zeros((N,3)) | |
for n in range(N): | |
data[n,:] = sensor._acceleration | |
def setup1(): | |
import board | |
import busio | |
import adafruit_bno055 # pip install adafruit-circuitpython-bno055 | |
i2c = busio.I2C(board.SCL, board.SDA) | |
sensor: adafruit_bno055.BNO055_I2C = adafruit_bno055.BNO055_I2C(i2c,0x29) | |
sensor.mode = adafruit_bno055.ACCONLY_MODE | |
sensor.accel_bandwidth = adafruit_bno055.ACCEL_125HZ | |
return sensor | |
if __name__ == "__main__": | |
t1 = timeit.Timer( | |
stmt='main1(sensor)', | |
setup="from __main__ import main1, setup1; sensor=setup1();") | |
n,T = t1.autorange() | |
print(f"took {T:.2f} seconds to run main1 {n:d} times ({N*n/T:.1f} Hz)") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment