Arduino Nano 33 Sense Rev2- IMU sensor
What is the OLEDIMU display?sensor?
Library
Ano OLEDuse displaythe IMU (Organicinertial Light-Emittingmeasurement Diodeunit) display)in isNano a33 screenBLE technology where each pixel emits its own light using organic carbon-based materials. Unlike LCDs, OLEDs do not need a backlight, which allows them to produce deeper blacks, higher contrast,Rev2 and thinner,Nano more33 flexibleBLE screens.
IfRev2, you wantneed to use the displayArduino_BMI270_BMM150 forlibrary animation, please reference this tutorial.
3.3V Logic
This OLED displays have a logic levelinstead of 3.3V.Arduino_LSM9DS1 If you use any classic Arduino (UNO and Leonardo) with 5V logic, the display may misbehave. 3.3V Arduino includes Due, Zero, Nano 33 and MKR series. In this tutorial, we will be using Nano 33 Sense Rev2.
OLED vs LCD
Replace | ||
#include | For more information about libraries for | |
Wiring
Wiring up the sensor is simple:
VCC to 3.3VGND to GNDSCL to SPI CLock Pin (Pin 13 on NanoNANO 33Sense)SENSE SDAREV2,topleaseSPI MOSI Pin (Pin 11 on Nano 33 Sense)RST to Pin 4DC to Pin 5CS to Pin 6
Library
To use this code you will need thevisit Adafruit_SH110X Libraryhere.
We have a tutorial on how to install a library here.
Getting started
This code is modified from the library example code SH1106_128x64_SPi_QTPY.
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#define OLED_MOSI 11
#define OLED_CLK 13
#define OLED_DC 5
#define OLED_CS 6
#define OLED_RST 4
Adafruit_SH1106G display = Adafruit_SH1106G(128, 64,OLED_MOSI, OLED_CLK, OLED_DC, OLED_RST, OLED_CS);
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2
#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH 16
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
B00000001, B11000000,
B00000001, B11000000,
B00000011, B11100000,
B11110011, B11100000,
B11111110, B11111000,
B01111110, B11111111,
B00110011, B10011111,
B00011111, B11111100,
B00001101, B01110000,
B00011011, B10100000,
B00111111, B11100000,
B00111111, B11110000,
B01111100, B11110000,
B01110000, B01110000,
B00000000, B00110000
};
void setup() {
Serial.begin(9600);
//display.setContrast (0); // dim display
// Start OLED
display.begin(0, true); // we dont use the i2c address but we will reset!
// Show image buffer on the display hardware.
// Since the buffer is intialized with an Adafruit splashscreen
// internally, this will display the splashscreen.
display.display();
delay(2000);
// Clear the buffer.
display.clearDisplay();
// draw a single pixel
display.drawPixel(10, 10, SH110X_WHITE);
// Show the display buffer on the hardware.
// NOTE: You _must_ call display after making any drawing commands
// to make them visible on the display hardware!
display.display();
delay(2000);
display.clearDisplay();
// draw many lines
testdrawline();
display.display();
delay(2000);
display.clearDisplay();
// draw rectangles
testdrawrect();
display.display();
delay(2000);
display.clearDisplay();
// draw multiple rectangles
testfillrect();
display.display();
delay(2000);
display.clearDisplay();
// draw mulitple circles
testdrawcircle();
display.display();
delay(2000);
display.clearDisplay();
// draw a SH110X_WHITE circle, 10 pixel radius
display.fillCircle(display.width() / 2, display.height() / 2, 10, SH110X_WHITE);
display.display();
delay(2000);
display.clearDisplay();
testdrawroundrect();
delay(2000);
display.clearDisplay();
testfillroundrect();
delay(2000);
display.clearDisplay();
testdrawtriangle();
delay(2000);
display.clearDisplay();
testfilltriangle();
delay(2000);
display.clearDisplay();
// draw the first ~12 characters in the font
testdrawchar();
display.display();
delay(2000);
display.clearDisplay();
// text display tests
display.setTextSize(1);
display.setTextColor(SH110X_WHITE);
display.setCursor(0, 0);
display.println("Failure is always an option");
display.setTextColor(SH110X_BLACK, SH110X_WHITE); // 'inverted' text
display.println(3.141592);
display.setTextSize(2);
display.setTextColor(SH110X_WHITE);
display.print("0x"); display.println(0xDEADBEEF, HEX);
display.display();
delay(2000);
display.clearDisplay();
// miniature bitmap display
display.drawBitmap(30, 16, logo16_glcd_bmp, 16, 16, 1);
display.display();
delay(1);
// invert the display
display.invertDisplay(true);
delay(1000);
display.invertDisplay(false);
delay(1000);
display.clearDisplay();
// draw a bitmap icon and 'animate' movement
testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_HEIGHT, LOGO16_GLCD_WIDTH);
}
void loop() {
}
void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h) {
uint8_t icons[NUMFLAKES][3];
// initialize
for (uint8_t f = 0; f < NUMFLAKES; f++) {
icons[f][XPOS] = random(display.width());
icons[f][YPOS] = 0;
icons[f][DELTAY] = random(5) + 1;
Serial.print("x: ");
Serial.print(icons[f][XPOS], DEC);
Serial.print(" y: ");
Serial.print(icons[f][YPOS], DEC);
Serial.print(" dy: ");
Serial.println(icons[f][DELTAY], DEC);
}
while (1) {
// draw each icon
for (uint8_t f = 0; f < NUMFLAKES; f++) {
display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SH110X_WHITE);
}
display.display();
delay(200);
// then erase it + move it
for (uint8_t f = 0; f < NUMFLAKES; f++) {
display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SH110X_BLACK);
// move it
icons[f][YPOS] += icons[f][DELTAY];
// if its gone, reinit
if (icons[f][YPOS] > display.height()) {
icons[f][XPOS] = random(display.width());
icons[f][YPOS] = 0;
icons[f][DELTAY] = random(5) + 1;
}
}
}
}
void testdrawchar(void) {
display.setTextSize(1);
display.setTextColor(SH110X_WHITE);
display.setCursor(0, 0);
for (uint8_t i = 0; i < 168; i++) {
if (i == '\n') continue;
display.write(i);
if ((i > 0) && (i % 21 == 0))
display.println();
}
display.display();
delay(1);
}
void testdrawcircle(void) {
for (int16_t i = 0; i < display.height(); i += 2) {
display.drawCircle(display.width() / 2, display.height() / 2, i, SH110X_WHITE);
display.display();
delay(1);
}
}
void testfillrect(void) {
uint8_t color = 1;
for (int16_t i = 0; i < display.height() / 2; i += 3) {
// alternate colors
display.fillRect(i, i, display.width() - i * 2, display.height() - i * 2, color % 2);
display.display();
delay(1);
color++;
}
}
void testdrawtriangle(void) {
for (int16_t i = 0; i < min(display.width(), display.height()) / 2; i += 5) {
display.drawTriangle(display.width() / 2, display.height() / 2 - i,
display.width() / 2 - i, display.height() / 2 + i,
display.width() / 2 + i, display.height() / 2 + i, SH110X_WHITE);
display.display();
delay(1);
}
}
void testfilltriangle(void) {
uint8_t color = SH110X_WHITE;
for (int16_t i = min(display.width(), display.height()) / 2; i > 0; i -= 5) {
display.fillTriangle(display.width() / 2, display.height() / 2 - i,
display.width() / 2 - i, display.height() / 2 + i,
display.width() / 2 + i, display.height() / 2 + i, SH110X_WHITE);
if (color == SH110X_WHITE) color = SH110X_BLACK;
else color = SH110X_WHITE;
display.display();
delay(1);
}
}
void testdrawroundrect(void) {
for (int16_t i = 0; i < display.height() / 2 - 2; i += 2) {
display.drawRoundRect(i, i, display.width() - 2 * i, display.height() - 2 * i, display.height() / 4, SH110X_WHITE);
display.display();
delay(1);
}
}
void testfillroundrect(void) {
uint8_t color = SH110X_WHITE;
for (int16_t i = 0; i < display.height() / 2 - 2; i += 2) {
display.fillRoundRect(i, i, display.width() - 2 * i, display.height() - 2 * i, display.height() / 4, color);
if (color == SH110X_WHITE) color = SH110X_BLACK;
else color = SH110X_WHITE;
display.display();
delay(1);
}
}
void testdrawrect(void) {
for (int16_t i = 0; i < display.height() / 2; i += 2) {
display.drawRect(i, i, display.width() - 2 * i, display.height() - 2 * i, SH110X_WHITE);
display.display();
delay(1);
}
}
void testdrawline() {
for (int16_t i = 0; i < display.width(); i += 4) {
display.drawLine(0, 0, i, display.height() - 1, SH110X_WHITE);
display.display();
delay(1);
}
for (int16_t i = 0; i < display.height(); i += 4) {
display.drawLine(0, 0, display.width() - 1, i, SH110X_WHITE);
display.display();
delay(1);
}
delay(250);
display.clearDisplay();
for (int16_t i = 0; i < display.width(); i += 4) {
display.drawLine(0, display.height() - 1, i, 0, SH110X_WHITE);
display.display();
delay(1);
}
for (int16_t i = display.height() - 1; i >= 0; i -= 4) {
display.drawLine(0, display.height() - 1, display.width() - 1, i, SH110X_WHITE);
display.display();
delay(1);
}
delay(250);
display.clearDisplay();
for (int16_t i = display.width() - 1; i >= 0; i -= 4) {
display.drawLine(display.width() - 1, display.height() - 1, i, 0, SH110X_WHITE);
display.display();
delay(1);
}
for (int16_t i = display.height() - 1; i >= 0; i -= 4) {
display.drawLine(display.width() - 1, display.height() - 1, 0, i, SH110X_WHITE);
display.display();
delay(1);
}
delay(250);
display.clearDisplay();
for (int16_t i = 0; i < display.height(); i += 4) {
display.drawLine(display.width() - 1, 0, 0, i, SH110X_WHITE);
display.display();
delay(1);
}
for (int16_t i = 0; i < display.width(); i += 4) {
display.drawLine(display.width() - 1, 0, i, display.height() - 1, SH110X_WHITE);
display.display();
delay(1);
}
delay(250);
}
Something fun
Nano 33 Sense Rev2 has built-in IMU sensor, temperature and humidity sensor and microphone. The code below will have a ball displayed on the OLED to follow Nano 33's motion.
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <Arduino_BMI270_BMM150.h>
#include <math.h>
// OLED SPI pins
#define OLED_MOSI 11
#define OLED_CLK 13
#define OLED_DC 5
#define OLED_CS 6
#define OLED_RST 4
Adafruit_SH1106G display = Adafruit_SH1106G(128, 64, OLED_MOSI, OLED_CLK, OLED_DC, OLED_RST, OLED_CS);
// -------- TUNING --------
const int W = 128;
const int H = 64;
const int BALL_RADIUS = 6;
// How strongly linear accel (in g or m/s^2 units) maps to pixels/sec^2.
// Increase to make ball accelerate more for the same hand motion.
const float LIN_ACCEL_TO_PIX = 220.0f;
// Low-pass alpha used to estimate gravity from accel: gravity = LPF(accel).
// Lower alpha -> smoother/slow gravity estimate. Typical 0.01..0.05
const float GRAV_LPF_ALPHA = 0.02f;
// Ignore very small linear accelerations (g) to reduce jitter
const float LINEAR_DEADZONE = 0.02f; // ~0.02 g
// Friction & bounce
const float FRICTION = 0.98f; // per-frame damping factor (closer to 1 = less friction)
const float BOUNCE_DAMP = 0.6f; // velocity retained after hitting wall
// Loop timing
const unsigned long LOOP_DELAY_MS = 5;
float gravX = 0.0f, gravY = 0.0f, gravZ = 0.0f; // gravity estimate (same units as IMU accel)
float vx = 0.0f, vy = 0.0f; // velocity in pixels/sec
float px = W/2.0f, py = H/2.0f; // position in pixels
unsigned long lastMillis = 0;
void setup() {
Serial.begin(115200);
display.begin(0, true);
display.clearDisplay();
display.display();
if (!IMU.begin()) {
Serial.println("Failed to initialize IMU!");
while (1);
}
// Let sensor settle, sample a bit for initial gravity
delay(200);
// Initialize gravity estimate with a few samples
for (int i=0; i<50; ++i) {
if (IMU.accelerationAvailable()) {
float ax, ay, az;
IMU.readAcceleration(ax, ay, az);
if (i==0) { gravX = ax; gravY = ay; gravZ = az; }
else {
gravX = gravX + GRAV_LPF_ALPHA * (ax - gravX);
gravY = gravY + GRAV_LPF_ALPHA * (ay - gravY);
gravZ = gravZ + GRAV_LPF_ALPHA * (az - gravZ);
}
}
delay(5);
}
lastMillis = millis();
display.clearDisplay();
display.fillCircle((int)round(px), (int)round(py), BALL_RADIUS, SH110X_WHITE);
display.display();
}
void loop() {
unsigned long now = millis();
float dt = (now - lastMillis) / 1000.0f;
if (dt <= 0.0f) dt = 0.001f;
lastMillis = now;
float ax = 0.0f, ay = 0.0f, az = 0.0f;
if (IMU.accelerationAvailable()) {
IMU.readAcceleration(ax, ay, az);
}
// Update gravity estimate with LPF: grav = grav + alpha*(acc - grav)
gravX = gravX + GRAV_LPF_ALPHA * (ax - gravX);
gravY = gravY + GRAV_LPF_ALPHA * (ay - gravY);
gravZ = gravZ + GRAV_LPF_ALPHA * (az - gravZ);
// Linear acceleration = measured - gravity (in sensor units)
float linX = ax - gravX;
float linY = ay - gravY;
float linZ = az - gravZ; // not used here but available
// Deadzone to reduce small noise
if (fabs(linX) < LINEAR_DEADZONE) linX = 0;
if (fabs(linY) < LINEAR_DEADZONE) linY = 0;
// Map linear accel to pixels/sec^2
// Note: choose mapping so motion of device moves the ball in the same direction.
// You might need to invert signs depending on your mounting/orientation.
//
// Common mapping (OLED on top of Nano 33, X axis left-right, Y axis forward-back):
// linX -> px (left/right)
// linY -> py (up/down)
//
// If the ball moves opposite to the board, invert sign(s) below.
float ax_pixels = linX * LIN_ACCEL_TO_PIX;
float ay_pixels = linY * LIN_ACCEL_TO_PIX;
// Integrate velocity (vx, vy) using accel (pixels/sec^2)
vx += ax_pixels * dt;
vy += ay_pixels * dt;
// Apply friction (scaled by dt so behaviour is consistent with loop time)
float frictionScaled = pow(FRICTION, dt * 60.0f);
vx *= frictionScaled;
vy *= frictionScaled;
// Integrate position
px += vx * dt;
py += vy * dt;
// Keep inside bounds (account for radius)
if (px < BALL_RADIUS) {
px = BALL_RADIUS;
if (vx < 0) vx = -vx * BOUNCE_DAMP;
} else if (px > W - 1 - BALL_RADIUS) {
px = W - 1 - BALL_RADIUS;
if (vx > 0) vx = -vx * BOUNCE_DAMP;
}
if (py < BALL_RADIUS) {
py = BALL_RADIUS;
if (vy < 0) vy = -vy * BOUNCE_DAMP;
} else if (py > H - 1 - BALL_RADIUS) {
py = H - 1 - BALL_RADIUS;
if (vy > 0) vy = -vy * BOUNCE_DAMP;
}
// Render
display.clearDisplay();
// border
display.drawRect(0, 0, W, H, SH110X_WHITE);
// ball
display.fillCircle((int)round(px), (int)round(py), BALL_RADIUS, SH110X_WHITE);
// small velocity vector for visualization
int vx_end_x = (int)round(px + vx * 0.02f);
int vy_end_y = (int)round(py + vy * 0.02f);
display.drawLine((int)round(px), (int)round(py), vx_end_x, vy_end_y, SH110X_WHITE);
display.display();
// Optional debug print periodically
static unsigned long lastPrint = 0;
if (now - lastPrint > 200) {
Serial.print("ax: "); Serial.print(ax, 4);
Serial.print(" ay: "); Serial.print(ay, 4);
Serial.print(" linX: "); Serial.print(linX, 5);
Serial.print(" linY: "); Serial.print(linY, 5);
Serial.print(" px: "); Serial.print(px, 2);
Serial.print(" py: "); Serial.print(py, 2);
Serial.print(" vx: "); Serial.print(vx, 3);
Serial.print(" vy: "); Serial.println(vy, 3);
lastPrint = now;
}
delay(LOOP_DELAY_MS);
}
