Skip to content

Instantly share code, notes, and snippets.

@el-hult
Last active February 23, 2023 10:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save el-hult/62ff50bc3f9aabf5378704ba75dc9f98 to your computer and use it in GitHub Desktop.
Save el-hult/62ff50bc3f9aabf5378704ba75dc9f98 to your computer and use it in GitHub Desktop.
/*
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;
}
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)")
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