/* Noch zu tun:
- Kommentare
- Photodiode value ändern, wenn montiert
- Shutterwerte anpassen an Realität
- Verifizieren der Werte
*/
#include <Arduino.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_I2CDevice.h>
#include <U8g2lib.h>
#include <Adafruit_GFX.h>
#include <OneButton.h>
#include <Adafruit_SPIDevice.h>
#include <AccelStepper.h>
#include <Servo.h>
/*
+++++++++++++++++++++++++++++++++++++++
Display (OLED 128x64)
pixel 8x8
input voltage 3,3 V
+++++++++++++++++++++++++++++++++++++++
*/
U8X8_SSD1309_128X64_NONAME2_4W_SW_SPI oled(12, 11, 10, 13, 9);
/*
+++++++++++++++++++++++++++++++++++++++
Stepper
input voltage 5 V
+++++++++++++++++++++++++++++++++++++++
*/
#define motorPin1 2
#define motorPin2 3
#define motorPin3 4
#define motorPin4 5
AccelStepper stepper(AccelStepper::HALF4WIRE, motorPin1, motorPin3, motorPin2, motorPin4);
long targetSteps; // Steps the stepper needs to reach for the right position. (0 at the lamp, 78848 at the rollerlever)
/*
+++++++++++++++++++++++++++++++++++++++
Servo
input voltage 5 V
+++++++++++++++++++++++++++++++++++++++
*/
#define servoPin 8
Servo myservo;
// angles don't make sense but are right
#define n 6 // nothing shut
#define r 25 // right shut, left irradiated
#define l 65 // left shut, right irradiated
#define m 180 // mid shut
/*
+++++++++++++++++++++++++++++++++++++++
Roller lever
input voltage 5 V
+++++++++++++++++++++++++++++++++++++++
*/
#define rollerlever 16
/*
+++++++++++++++++++++++++++++++++++++++
Encoder
input voltage 5 V
+++++++++++++++++++++++++++++++++++++++
*/
#define inputCLKEncoder 19
#define inputDTEncoder 18
#define inputSWEncoder 17
float rotatedCounterE = 0.4; // at beginning, between 0.4 and 1.2 mW/cm²
uint16_t rotatedCountert = 1; // at beginning, between 1 and 999 s
uint8_t rotatedCounters = 3; // at beginning, between 1 and 4; 1 = left, 2 = right, 3 = mid, 4 = nothing
uint8_t currentStateCLK;
uint8_t previousStateCLK;
uint8_t pressedCounter[] = {13, 11, 8, 0}; // y-Coordinate of the OLED to write in
uint8_t i = 0; // counter to go through pressedCounter[]
float setdistance; // between 1.41 and 8.39 (15.4 at rollerlever) cm
float dose;
OneButton button(inputSWEncoder, true);
/*
+++++++++++++++++++++++++++++++++++++++
speaker
input voltage 5 V
+++++++++++++++++++++++++++++++++++++++
*/
#define speaker 6
/*
+++++++++++++++++++++++++++++++++++++++
Photodiode JIC269C
input voltage 5 V
+++++++++++++++++++++++++++++++++++++++
*/
#define photodiode A6
int photodiodeValue; // value that the photodiode messures
float currentPhotodiodeVoltage; // voltage that is calculated by the photodiodeValue
const float expectedPhotodiodeVoltage = 2.568; // voltage that is expected
float deviation; // deviation of the expected irradiation and the actual irradiation in percent
/*
+++++++++++++++++++++++++++++++++++++++
Functiondeclarations
+++++++++++++++++++++++++++++++++++++++
*/
void startDisplay(); // Function starts the display and shows the topics.
void setValue(); // Function changes the value in the marked line.
void goBackValue(); // Function if the encoder gets double clicked. Chosen value is logged in and you can change the value above.
void validateValue(); // Function if the encoder gets clicked. Chosen value is logged in and you can change the value below.
void setShutterpos(uint8_t); // Function sets the old Shutterposition to the new Shutterposition.
void validateShutterpos(uint8_t); // Function validates the chosen Shutterposition before the next value can be changed.
void getDistance(float); // Function calculates the distance based on E and prints the new value.
void moveRelative(float); // Function moves the sample to the chosen value by the stepper.
void getPhotodiodeValue(); // Function reads the value of the photodiode and compares it with the expected value.
static uint8_t *rotate90(uint8_t *buf) // Function rotates the text on OLED by 90°.
{
static uint8_t rbuf[8];
uint8_t i, h;
uint8_t *p;
for (i = 0; i < 8; i++)
rbuf[i] = 0;
for (i = 0; i < 8; i++)
{
h = buf[i];
p = rbuf;
*p >>= 1;
*p |= (h & 128);
h <<= 1;
p++;
*p >>= 1;
*p |= (h & 128);
h <<= 1;
p++;
*p >>= 1;
*p |= (h & 128);
h <<= 1;
p++;
*p >>= 1;
*p |= (h & 128);
h <<= 1;
p++;
*p >>= 1;
*p |= (h & 128);
h <<= 1;
p++;
*p >>= 1;
*p |= (h & 128);
h <<= 1;
p++;
*p >>= 1;
*p |= (h & 128);
h <<= 1;
p++;
*p >>= 1;
*p |= (h & 128);
h <<= 1;
p++;
}
return rbuf;
}
void u8x8_draw_glyph_90(u8x8_t *u8x8, uint8_t x, uint8_t y, uint8_t encoding)
{
static uint8_t buf[8];
u8x8_get_glyph_data(u8x8, encoding, buf, 0);
u8x8_DrawTile(u8x8, x, y, 1, rotate90(buf));
}
void u8x8_draw_string_90(u8x8_t *u8x8, uint8_t x, uint8_t y, const char *s)
{
while (*s != '\0')
u8x8_draw_glyph_90(u8x8, x, y++, *s++);
}
/*
+++++++++++++++++++++++++++++++++++++++
Setup
+++++++++++++++++++++++++++++++++++++++
*/
void setup()
{
pinMode(rollerlever, INPUT_PULLUP);
pinMode(inputCLKEncoder, INPUT);
pinMode(inputDTEncoder, INPUT);
pinMode(photodiode, INPUT);
button.attachClick(validateValue);
button.attachDoubleClick(goBackValue);
button.setDebounceTicks(20);
Serial.begin(9600); // Serial display //löschbar
previousStateCLK = digitalRead(inputCLKEncoder);
stepper.setMaxSpeed(1500);
stepper.setAcceleration(2000);
myservo.attach(servoPin);
Serial.println(myservo.read());
myservo.write(m); // servo shuts the mid
Serial.println(myservo.read());
oled.begin();
oled.setFont(u8x8_font_5x7_f);
u8x8_draw_string_90(oled.getU8x8(), 9, 0, "wait"); // line 9
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "for"); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "setup"); // line 7
u8x8_draw_string_90(oled.getU8x8(), 6, 0, "..."); // line 6
u8x8_draw_string_90(oled.getU8x8(), 5, 0, "<2min"); // line 5
delay(1999);
while (1) // go to startposition
{
if (digitalRead(rollerlever) == LOW)
{
break;
}
stepper.move(200); // rotate clockwise to the back
stepper.run();
}
stepper.setCurrentPosition(78848); // number of steps for the current distance
Serial.println("setup completed");
startDisplay();
}
/*
+++++++++++++++++++++++++++++++++++++++
Loop
+++++++++++++++++++++++++++++++++++++++
*/
void loop()
{
button.tick();
setValue();
}
/*
+++++++++++++++++++++++++++++++++++++++
Functions
+++++++++++++++++++++++++++++++++++++++
*/
// Function starts the display and shows the topics.
void startDisplay()
{
oled.begin();
u8x8_draw_string_90(oled.getU8x8(), 14, 0, "E="); // text to display, line 14
u8x8_draw_string_90(oled.getU8x8(), 12, 0, "t="); // line 12
u8x8_draw_string_90(oled.getU8x8(), 10, 0, "covered"); // line 10
u8x8_draw_string_90(oled.getU8x8(), 9, 0, "side:");
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "l[] r[] "); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "m[X] n[]"); // line 7
u8x8_draw_string_90(oled.getU8x8(), 5, 0, "d="); // line 5
getDistance(rotatedCounterE);
u8x8_draw_string_90(oled.getU8x8(), 3, 0, "Dev E="); // line 3
getPhotodiodeValue();
u8x8_draw_string_90(oled.getU8x8(), 0, 0, "[START]"); // line 0
u8x8_draw_string_90(oled.getU8x8(), 11, 0, "1 "); // line 11
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 13, 0, "0.40"); // line 13
}
// Function to change the value in the marked line.
void setValue()
{
currentStateCLK = digitalRead(inputCLKEncoder);
switch (pressedCounter[i])
{
case 13:
if (currentStateCLK != previousStateCLK)
{
if (digitalRead(inputDTEncoder) != currentStateCLK && rotatedCounterE < 1.19)
{
rotatedCounterE = rotatedCounterE + 0.05;
Serial.println("+"); // löschbar
}
else if (digitalRead(inputDTEncoder) == currentStateCLK && rotatedCounterE > 0.4)
{
rotatedCounterE = rotatedCounterE - 0.05;
Serial.println("-"); // löschbar
}
getDistance(rotatedCounterE);
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 13, 0, String(rotatedCounterE).c_str()); // line 13
delay(200); // Debounce
}
break;
case 11:
if (currentStateCLK != previousStateCLK)
{
if (digitalRead(inputDTEncoder) != currentStateCLK && rotatedCountert < 999)
{
rotatedCountert = rotatedCountert + 1;
Serial.println("+"); // löschbar
}
else if (digitalRead(inputDTEncoder) == currentStateCLK && rotatedCountert > 1)
{
rotatedCountert = rotatedCountert - 1;
Serial.println("-"); // löschbar
}
u8x8_draw_string_90(oled.getU8x8(), 11, 0, " "); // line 11
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 11, 0, String(rotatedCountert).c_str()); // line 11
delay(200); // Debounce
}
break;
case 8:
if (currentStateCLK != previousStateCLK)
{
if (digitalRead(inputDTEncoder) != currentStateCLK && rotatedCounters < 4)
{
rotatedCounters = rotatedCounters + 1;
Serial.println("+"); // löschbar
}
else if (digitalRead(inputDTEncoder) == currentStateCLK && rotatedCounters > 1)
{
rotatedCounters = rotatedCounters - 1;
Serial.println("-"); // löschbar
}
setShutterpos(rotatedCounters);
delay(200); // Debounce
}
break;
}
}
// Function if the encoder gets clicked. Chosen value is logged in and you can change the value below.
void validateValue()
{
Serial.println("weiter"); // löschbar
if (pressedCounter[i] == 13 || pressedCounter[i] == 11)
{
oled.setInverseFont(0); // previous value logged
switch (pressedCounter[i])
{
case 13:
u8x8_draw_string_90(oled.getU8x8(), 13, 0, String(rotatedCounterE).c_str()); // line 13
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 11, 0, " "); // line 11
u8x8_draw_string_90(oled.getU8x8(), 11, 0, String(rotatedCountert).c_str()); // line 11
break;
case 11:
u8x8_draw_string_90(oled.getU8x8(), 11, 0, " "); // line 11
u8x8_draw_string_90(oled.getU8x8(), 11, 0, String(rotatedCountert).c_str()); // line 11
setShutterpos(rotatedCounters);
break;
}
}
if (pressedCounter[i] == 8)
{
validateShutterpos(rotatedCounters);
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 0, 0, "[START] "); // line 0
}
if (pressedCounter[i] == 0)
{
u8x8_draw_string_90(oled.getU8x8(), 0, 0, " "); // line 0
u8x8_draw_string_90(oled.getU8x8(), 0, 2, "WAIT"); // line 0, irradiation is running
moveRelative(setdistance); // move sample to calculated distance by the stepper
if (setdistance <= 14.6)
{ // Limitation by the program (at this point the distance is actually not possible)
while (stepper.isRunning())
{
stepper.run();
Serial.println("Go"); // löschbar
delay(10);
}
switch (rotatedCounters)
{ // move shutter into chosen position by the servo
case 1:
myservo.write(l); // left shut
Serial.println(myservo.read());
delay(5000);
break;
case 2:
myservo.write(r); // right shut
Serial.println(myservo.read());
delay(5000);
break;
case 3:
myservo.write(m); // mid shut
Serial.println(myservo.read());
delay(5000);
break;
case 4:
myservo.write(n); // nothing shut
Serial.println(myservo.read());
delay(5000);
break;
}
delay(rotatedCountert * 1000); // irradiation is running!
myservo.write(m); // shut the shutter again
delay(200);
u8x8_draw_string_90(oled.getU8x8(), 0, 0, " "); // line 0
u8x8_draw_string_90(oled.getU8x8(), 0, 2, "DONE"); // line 0, irradiation finished
for (int t = 0; t <= 2; t++)
{
tone(speaker, 440); // Sound on
delay(800);
noTone(speaker); // Sound off
delay(800);
}
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 0, 0, " "); // line 0
u8x8_draw_string_90(oled.getU8x8(), 0, 0, "[AGAIN]"); // line 0, Start again or change values!
}
if (setdistance > 15.4 || setdistance < 0)
{ // Distance not possible --> roller level, change E!
u8x8_draw_string_90(oled.getU8x8(), 0, 0, " "); // line 0
u8x8_draw_string_90(oled.getU8x8(), 0, 0, "CHANGE E"); // line 0
;
}
}
if (i < 3)
{
i++;
}
}
// Function if the encoder gets double clicked. Chosen value is logged in and you can change the value above.
void goBackValue()
{
Serial.println("zurück"); // löschbar
oled.setInverseFont(0);
switch (pressedCounter[i])
{
case 11:
u8x8_draw_string_90(oled.getU8x8(), 11, 0, " "); // line 11
u8x8_draw_string_90(oled.getU8x8(), 11, 0, String(rotatedCountert).c_str()); // line 11
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 13, 0, String(rotatedCounterE).c_str()); // line 13
break;
case 8:
validateShutterpos(rotatedCounters);
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 11, 0, " "); // line 11
u8x8_draw_string_90(oled.getU8x8(), 11, 0, String(rotatedCountert).c_str()); // line 11
break;
case 0:
u8x8_draw_string_90(oled.getU8x8(), 0, 0, " "); // line 0
u8x8_draw_string_90(oled.getU8x8(), 0, 0, "[START]"); // line 0
oled.setInverseFont(1);
setShutterpos(rotatedCounters);
break;
}
if (i > 0)
{
i--;
}
}
// Function sets the old Shutterposition to the new Shutterposition.
void setShutterpos(uint8_t rotatedCounters)
{
oled.setInverseFont(0);
switch (rotatedCounters)
{
case 1:
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "l[X] r[]"); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "m[] n[] "); // line 7
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 8, 2, "X");
break;
case 2:
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "l[] r[X]"); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "m[] n[] "); // line 7
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 8, 6, "X");
break;
case 3:
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "l[] r[] "); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "m[X] n[]"); // line 7
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 7, 2, "X");
break;
case 4:
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "l[] r[] "); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "m[] n[X]"); // line 7
oled.setInverseFont(1);
u8x8_draw_string_90(oled.getU8x8(), 7, 6, "X");
break;
}
}
// Function validates the chosen Shutterposition before the next value can be changed.
void validateShutterpos(uint8_t rotatedCounters)
{
oled.setInverseFont(0);
switch (rotatedCounters)
{
case 1:
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "l[X] r[]"); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "m[] n[] "); // line 7
break;
case 2:
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "l[] r[X]"); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "m[] n[] "); // line 7
break;
case 3:
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "l[] r[] "); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "m[X] n[]"); // line 7
break;
case 4:
u8x8_draw_string_90(oled.getU8x8(), 8, 0, "l[] r[] "); // line 8
u8x8_draw_string_90(oled.getU8x8(), 7, 0, "m[] n[X]"); // line 7
break;
}
}
// Function calculates the distance based on E and prints the new value.
void getDistance(float rotatedCounterE)
{ // highest possible E = 1.2 mW/cm², distance 1.41 cm, lowest possible E = 0.4 mW/cm², distance 8.39 cm
setdistance = (169.01 - sqrt(pow(169.01, 2) - 4 * 5.5552 * (1426.7 - rotatedCounterE * 1000))) / (2 * 5.552); // determined formula for a distance of 2 - 10 cm
oled.setInverseFont(0);
u8x8_draw_string_90(oled.getU8x8(), 4, 0, String(setdistance).c_str()); // line 4
}
// Function moves the sample to the chosen value by the stepper.
void moveRelative(float setdistance)
{
targetSteps = (setdistance / 0.0001953125); // needed steps to reach the distance; spindle pitch = 0.8 cm; 0.0001953125 mm per Step; 4096 Steps per revolution
stepper.runToNewPosition(targetSteps);
}
// Function reads the value of the photodiode and compares it with the expected value.
void getPhotodiodeValue()
{
photodiodeValue = analogRead(photodiode);
delay(100);
currentPhotodiodeVoltage = photodiodeValue * (5.0 / 1024.0); // voltage in Volt (0-5 V); 2^10 bit
deviation = (currentPhotodiodeVoltage / expectedPhotodiodeVoltage) * 100; // deviation in percent
Serial.println(currentPhotodiodeVoltage); // löschbar
Serial.println(deviation); // löschbar
oled.setInverseFont(0);
u8x8_draw_string_90(oled.getU8x8(), 2, 0, String(deviation).c_str()); // line 2
}