// Bed Leveling Tool
// Based on a design by Dominick Lee (https://www.hackster.io/solutionhacker/3d-print-bed-leveling-tool-using-m5stickc-035185)
//
//
// V1: jlb (jbrad2089@gmail.com)
// - Wrote software for a ATtiny1614 and 128x32 I2C OLED display
//
/**
* ATTiny1614 Pins mapped to Ardunio Pins
*
* +--------+
* VCC + 1 14 + GND
* (SS) 0 PA4 + 2 13 + PA3 10 (SCK)
* 1 PA5 + 3 12 + PA2 9 (MISO)
* (DAC) 2 PA6 + 4 11 + PA1 8 (MOSI)
* 3 PA7 + 5 10 + PA0 11 (UPDI)
* (RXD) 4 PB3 + 6 9 + PB0 7 (SCL)
* (TXD) 5 PB2 + 7 8 + PB1 6 (SDA)
* +--------+
*/
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <avr/sleep.h>
#include <EEPROM.h>
#define CAL_BTN 0 //PA4 D0
#define ON_BTN 1 //PA5 D1
#define SENSOR 9 //PA2 D9
#define OLED_I2C_ADDRESS 0x3C // OLED display I2C address
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define COLUMN_WIDTH 8 // Width of column on screen
#define COLUMN_HEIGHT SCREEN_WIDTH
#define LEVEL_WIDTH 4 // Width of requested level arrow
#define LEVEL_HEIGHT 4 // Height of requested level arrow
#define ARROW_X 22
#define ARROW_WIDTH 8
#define ARROW_HEIGHT 32
#define EPISLON 4 //Height of Thumbd Up area
//EEPROM handling
#define EEPROM_ADDRESS 0
#define EEPROM_MAGIC 0x0BAD0DAD
typedef struct {
uint32_t magic;
int requestedLevel; //Pressure recorded when cal button is pressed
} EEPROM_DATA;
EEPROM_DATA EepromData; //Current EEPROM settings
int pressureLevel; //Current pressure reading
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
//Thumbs Up (24 bits x 19 rows)
#define BITMAP_W 24
#define BITMAP_H 19
const uint8_t thumbsUp[] PROGMEM = {
0x1f, 0xff, 0x7f, 0x10, 0x00, 0xc1, 0x20, 0x00, 0x41, 0x3c, 0x00, 0x41, 0x40, 0x00, 0x41, 0x40,
0x00, 0x41, 0x7e, 0x00, 0x41, 0x40, 0x00, 0x41, 0x40, 0x00, 0x41, 0x7e, 0x01, 0xc1, 0x40, 0x03,
0x7f, 0x40, 0x06, 0x00, 0x7f, 0x8c, 0x00, 0x00, 0x88, 0x00, 0x00, 0x98, 0x00, 0x00, 0x90, 0x00,
0x00, 0x90, 0x00, 0x00, 0x90, 0x00, 0x00, 0x60, 0x00
};
//000111111111111101111111 0x1f, 0xff, 0x7f
//000100000000000011000001 0x10, 0x0, 0xc1
//001000000000000001000001 0x20, 0x0, 0x41
//001111000000000001000001 0x3c, 0x0, 0x41
//010000000000000001000001 0x40, 0x0, 0x41
//010000000000000001000001 0x40, 0x0, 0x41
//011111100000000001000001 0x7e, 0x0, 0x41
//010000000000000001000001 0x40, 0x0, 0x41
//010000000000000001000001 0x40, 0x0, 0x41
//011111100000000111000001 0x7e, 0x1, 0xc1
//010000000000001101111111 0x40, 0x3, 0x7f
//010000000000011000000000 0x40, 0x6, 0x0
//011111111001100000000000 0x7f, 0x8c, 0x0
//000000001000100000000000 0x0, 0x88, 0x0
//000000001001100000000000 0x0, 0x98, 0x0
//000000001001000000000000 0x0, 0x90, 0x0
//000000001001000000000000 0x0, 0x90, 0x0
//000000001001000000000000 0x0, 0x90, 0x0
//000000000110000000000000 0x0, 0x60, 0x0
//-----------------------------------------------------------------------------
//Handle pin change interrupt when ON button is pressed
// - This will wake up the microprocessor
// - Code will continue after the sleep_mode() command in the systemSleep() function.
void SwitchInterrupt()
{
}
//-----------------------------------------------------------------------------
//Initialise Hardware
void setup()
{
//Serial.begin(9600);
//ON button Interrupt will wake up from sleep mode
pinMode(ON_BTN, INPUT_PULLUP);
pinMode(CAL_BTN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ON_BTN),SwitchInterrupt,CHANGE);
while (!display.begin(0, OLED_I2C_ADDRESS))
{
//Serial.println(F("SSD1306 allocation failed"));
systemSleep();
}
display.setRotation(2);
//Eprom
readEepromData();
}
//-----------------------------------------------------------------------------
//Main Program Loop
void loop()
{
while (digitalRead(ON_BTN) == HIGH)
{
//Display current pressure as a bar
int pressureLevel = map(analogRead(SENSOR), 0, 1024, 0, COLUMN_HEIGHT);
display.clearDisplay();
display.drawRect(0, 0, COLUMN_HEIGHT, COLUMN_WIDTH, SSD1306_WHITE);
display.fillRect(0, 2, pressureLevel, COLUMN_WIDTH - 4, SSD1306_WHITE);
//Test if calibration button pressed
if (digitalRead(CAL_BTN) == LOW)
{
delay(10); //debounce
if (digitalRead(CAL_BTN) == LOW)
{
//Store the current level
EepromData.requestedLevel = pressureLevel;
writeEepromData();
displayCalibrationArrow(EepromData.requestedLevel);
display.display();
//Wait until button is released
while (digitalRead(CAL_BTN) == LOW)
{
delay(100);
}
}
}
//Show any calibration arrow
if (EepromData.requestedLevel != 0)
{
displayCalibrationArrow(EepromData.requestedLevel);
if (abs(pressureLevel - EepromData.requestedLevel) < EPISLON)
{
displayThumbsUp();
}
else if (pressureLevel > EepromData.requestedLevel)
{
//Gap too small, lower the bed to increase the gap
displayDownArrow();
}
else
{
//Gap too big, raise the bed to decrease the gap
displayUpArrow();
}
}
display.display();
delay(100);
}
//Wait until ON button is released
while (digitalRead(ON_BTN) == LOW)
{
delay(100);
}
//Shut down unit
systemSleep();
setup();
}
//-----------------------------------------------------------------------------
// display the calibration arrow on the sceen
void displayCalibrationArrow(int y)
{
display.fillTriangle(y, COLUMN_WIDTH, y - LEVEL_HEIGHT / 2, COLUMN_WIDTH + LEVEL_WIDTH, y + LEVEL_HEIGHT / 2, COLUMN_WIDTH + LEVEL_WIDTH, SSD1306_WHITE);
}
//-----------------------------------------------------------------------------
// display the lower bed arrow on the sceen
void displayDownArrow()
{
display.fillTriangle(0, ARROW_X, ARROW_WIDTH, ARROW_X - ARROW_WIDTH / 2, ARROW_WIDTH, ARROW_X + ARROW_WIDTH / 2, SSD1306_WHITE);
display.fillRect(ARROW_WIDTH, ARROW_X - ARROW_WIDTH / 4, ARROW_HEIGHT - ARROW_WIDTH, ARROW_WIDTH / 2 + 1, SSD1306_WHITE);
}
//-----------------------------------------------------------------------------
// display the raise bed arrow on the sceen
void displayUpArrow()
{
display.fillTriangle(COLUMN_HEIGHT, ARROW_X, COLUMN_HEIGHT - ARROW_WIDTH, ARROW_X - ARROW_WIDTH / 2, COLUMN_HEIGHT - ARROW_WIDTH, ARROW_X + ARROW_WIDTH / 2, SSD1306_WHITE);
display.fillRect(COLUMN_HEIGHT - ARROW_HEIGHT, ARROW_X - ARROW_WIDTH / 4, ARROW_HEIGHT - ARROW_WIDTH, ARROW_WIDTH / 2 + 1, SSD1306_WHITE);
}
//-----------------------------------------------------------------------------
// display the stop icon on the sceen
void displayThumbsUp()
{
display.drawBitmap((COLUMN_HEIGHT - BITMAP_H) / 2, ARROW_X - BITMAP_W / 2, thumbsUp, BITMAP_W, BITMAP_H, SSD1306_WHITE);
}
//-----------------------------------------------------------------------------
//Shut down OLED and put ATtiny to sleep
//Will wake up when LEFT button is pressed
void systemSleep()
{
interrupts();
display.clearDisplay();
display.display();
display.ssd1306_command(0xAE);
//cbi(ADCSRA,ADEN); // switch Analog to Digitalconverter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_enable();
sleep_mode(); // System actually sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
//sbi(ADCSRA,ADEN); // switch Analog to Digitalconverter ON
display.ssd1306_command(0xAF);
//Wait until on button is released
while (digitalRead(ON_BTN) == LOW)
{
delay(100);
}
}
//---------------------------------------------------------------
//Write the EepromData structure to EEPROM
void writeEepromData()
{
//This function uses EEPROM.update() to perform the write, so does not rewrites the value if it didn't change.
EEPROM.put(EEPROM_ADDRESS,EepromData);
}
//---------------------------------------------------------------
//Read the EepromData structure from EEPROM, initialise if necessary
void readEepromData()
{
//Eprom
EEPROM.get(EEPROM_ADDRESS,EepromData);
if (EepromData.magic != EEPROM_MAGIC)
{
EepromData.magic = EEPROM_MAGIC;
EepromData.requestedLevel = 0;
writeEepromData();
}
}
Comments