Inclinometer with Sony Spresense

Measure angle of your tool

 

Inclinometer with Sony Spresense

Things used in this project

 

Hardware components

HARDWARE LIST
Sony Spresense boards (main & extension)
DFRobot 6 DOF Sensor - MPU6050
ElectroPeak 0.96" OLED 64x128 Display Module
Button
Resistor 220 ohm
Magnet

Software apps and online services

 

Arduino IDE

Story

 

An inclinometer is an instrument used for measuring angles of an object with respect to a base. It is also known as a level gauge or bevel meter. In my case, I need get a very precise angle of the cuttings from my table saw. You can buy a level gauge, or create a simple one by yourself.

 

https://www.waltswooddesign.com/wp-content/uploads/2022/07/Spresense-Inclinometer.mp4

The Sony Spresense runs on 3v, which saves a lot of battery power, and is fine for the gyroscope + OLED.

 

Wiring all together

 

 

The two devices (gyroscope and display) are communicating using I2C. You can wire them in parallel, to address both at the same time. An additional resistor is not required for the Sony Spresense.

OLED

CODE
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET     -1    // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_WIDTH   128   // OLED display width, in pixels
#define SCREEN_HEIGHT  64    // OLED display height, in pixels
#define SCREEN_ADDRESS 0x3C  // 0x3D for 128x64, 0x3C for 128x32

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void initializeDisplay();
void loopDisplay();
void printAngle();

void setup(){
  Serial.begin(9600);
  initializeDisplay();
}

void loop(){
  loopDisplay();
  printAngle();
  delay(10);
}

void initializeDisplay() {
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  } else {
    Serial.println(F("OLED display found"));
  }

  display.clearDisplay();

  display.setTextSize(4);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println("Hello, world!");
  display.display(); 
}

void loopDisplay() {
}

Gyroscope

CODE
#include <SPI.h>
#include <Wire.h>

void initializeGyros();
void loopGyros();
const int MPU_addr=0x68;
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
int minVal=265;
int maxVal=402;
double x;
double y;
double z;

void setup(){
  Serial.begin(9600);
  initializeGyros();
}

void loop(){
  loopGyros();
  delay(10);
}

void initializeGyros() {
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
}

void loopGyros() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);
  AcX=Wire.read()<<8|Wire.read();
  AcY=Wire.read()<<8|Wire.read();
  AcZ=Wire.read()<<8|Wire.read();
  int xAng = map(AcX,minVal,maxVal,-90,90);
  int yAng = map(AcY,minVal,maxVal,-90,90);
  int zAng = map(AcZ,minVal,maxVal,-90,90);
   
  x= RAD_TO_DEG * (atan2(-yAng, -zAng)+PI);
  y= RAD_TO_DEG * (atan2(-xAng, -zAng)+PI);
  z= RAD_TO_DEG * (atan2(-yAng, -xAng)+PI);
   
  Serial.print(x);
  Serial.print(",");
  Serial.print(y);
  Serial.print(",");
  Serial.println(z);

  readings[readIndex] = z;
  readIndex++;
  if (readIndex == numReadings) { readIndex = 0; }
}

Button

 

The button is used to set the base (default) angle. A simple smooth algorithm is used to flatten the readings.

CODE
#define BUTTON_PIN     27    // input for button

const int numReadings  = 20;
double readings [numReadings];
int readIndex  = 0;
double smooth();

void setup(){
  Serial.begin(9600);
  initializeButton();
}

void loop(){
  loopButton();
  printAngle();
  delay(10);
}

void initializeButton() {
  pinMode(BUTTON_PIN, INPUT);
}

void loopButton() {
  bool clicked = digitalRead(BUTTON_PIN);
  if (clicked) {
    offset = smooth();
  }
}

void printAngle() {
  double real = abs(smooth() - offset);
  if (real > 180) { real = real - 180; }
  Serial.println(real);
}

double smooth() {
  double average;
  for (int i = 0; i < numReadings; i++) {
    average += readings[i];
  }
  average = average / numReadings;
  return average;
}

Finalize

 

Create a box to fit all components inside and use an USB power station to provide the power.

 

 

Schematics

 

Schematics

 


 

Code

 

main.ino

Arduino

CODE
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET     -1    // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_WIDTH   128   // OLED display width, in pixels
#define SCREEN_HEIGHT  64    // OLED display height, in pixels
#define SCREEN_ADDRESS 0x3C  // 0x3D for 128x64, 0x3C for 128x32
#define BUTTON_PIN     27    // input for button

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void initializeDisplay();
void loopDisplay();
void printAngle();

void initializeGyros();
void loopGyros();
const int MPU_addr=0x68;
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
int minVal=265;
int maxVal=402;
double x;
double y;
double z;

void initializeButton();
void loopButton();
double offset = 0;

const int numReadings  = 20;
double readings [numReadings];
int readIndex  = 0;
double smooth();

void setup(){
  Serial.begin(9600);

  initializeButton();
  initializeDisplay();
  initializeGyros();
}

void loop(){
  loopButton();
  loopGyros();
  loopDisplay();
  printAngle();
  delay(10);
}

// >>>>>>>>>>>> Display >>>>>>>>>>>>>>>

void initializeDisplay() {
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;);
  } else {
    Serial.println(F("OLED display found"));
  }

  display.clearDisplay();

  display.setTextSize(4);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println("Hello, world!");
  display.display(); 
}

void loopDisplay() {
}

// <<<<<<<<<<<< Display <<<<<<<<<<<<<<<
// >>>>>>>>>>>> Gyros   >>>>>>>>>>>>>>>

void initializeGyros() {
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);
}

void loopGyros() {
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);
  AcX=Wire.read()<<8|Wire.read();
  AcY=Wire.read()<<8|Wire.read();
  AcZ=Wire.read()<<8|Wire.read();
  int xAng = map(AcX,minVal,maxVal,-90,90);
  int yAng = map(AcY,minVal,maxVal,-90,90);
  int zAng = map(AcZ,minVal,maxVal,-90,90);
   
  x= RAD_TO_DEG * (atan2(-yAng, -zAng)+PI);
  y= RAD_TO_DEG * (atan2(-xAng, -zAng)+PI);
  z= RAD_TO_DEG * (atan2(-yAng, -xAng)+PI);
   
  Serial.print(x);
  Serial.print(",");
  Serial.print(y);
  Serial.print(",");
  Serial.println(z);

  readings[readIndex] = z;
  readIndex++;
  if (readIndex == numReadings) { readIndex = 0; }
}

// <<<<<<<<<<<< Gyros <<<<<<<<<<<<<<<
// >>>>>>>>>>>> Button >>>>>>>>>>>>>>>

void initializeButton() {
  pinMode(BUTTON_PIN, INPUT);
}

void loopButton() {
  bool clicked = digitalRead(BUTTON_PIN);
  if (clicked) {
    offset = smooth();
  }
}

// <<<<<<<<<<<< Button <<<<<<<<<<<<<<<

void printAngle() {
  double real = abs(smooth() - offset);
  if (real > 180) { real = real - 180; }
  display.clearDisplay();
  // display.getInterface().flipHorizontal();
  display.setTextSize(1);
  display.setCursor(29, 0);
  display.println(F("Inclinometer"));
  display.setTextSize(1);
  display.setCursor(0, 16);
  display.println(real);
  display.setCursor(0, 32);
  display.println(z);
  display.setCursor(0, 48);
  display.println(offset);
  display.display(); 
}

double smooth() {
  double average;
  for (int i = 0; i < numReadings; i++) {
    average += readings[i];
  }
  average = average / numReadings;
  return average;
}

The article was first published in hackster, Jul 28, 2022

cr: https://www.hackster.io/walwode/inclinometer-with-sony-spresense-90e211

author: Walter Heger


 

License
All Rights
Reserved
licensBg
0