uni

Thing1's amazing uni repo
Log | Files | Refs

commit 682c8619c30d773714f7577f4f60206166b0b374
parent 2a0b1095822fddbb355cbdd691aca760168d7f6a
Author: thing1 <thing1@seacrossedlovers.xyz>
Date:   Mon,  8 Dec 2025 10:04:51 +0000

Merge branch 'master' of seacrossedlovers.xyz:uni
merge

Diffstat:
M.gitignore | 1+
MCS12020/robot/line/Makefile | 4++--
DCS12020/robot/line/NOTE | 1-
DCS12020/robot/line/TODO | 5-----
DCS12020/robot/line/checklist | 25-------------------------
MCS12020/robot/line/line.ino | 4++--
ACS12020/robot/report/README.md | 8++++++++
ACS12020/robot/report/calibrate/calibrate.ino | 33+++++++++++++++++++++++++++++++++
ACS12020/robot/report/calibrate/helper.h | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ACS12020/robot/report/code/code.ino | 356+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ACS12020/robot/report/code/helper.h | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ACS12020/robot/report/code/reading.h | 36++++++++++++++++++++++++++++++++++++
MCS12020/robot/report/writeup/writeup.tex | 468++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
13 files changed, 1017 insertions(+), 181 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,4 @@ +*.zip *.ps *.pdf *.html diff --git a/CS12020/robot/line/Makefile b/CS12020/robot/line/Makefile @@ -1,10 +1,10 @@ all: compile upload upload: compile - arduino-cli upload --fqbn arduino:avr:uno . -p /dev/ttyACM1 + arduino-cli upload --fqbn arduino:avr:uno . -p /dev/ttyACM0 compile: line.ino arduino-cli compile --fqbn arduino:avr:uno . monitor: - arduino-cli monitor -p /dev/ttyACM1 --fqbn arduino:avr:uno + arduino-cli monitor -p /dev/ttyACM0 --fqbn arduino:avr:uno diff --git a/CS12020/robot/line/NOTE b/CS12020/robot/line/NOTE @@ -1 +0,0 @@ -This is for the 1 thick line paper diff --git a/CS12020/robot/line/TODO b/CS12020/robot/line/TODO @@ -1,4 +0,0 @@ -- make a funciton called flash LED - - this should set a global var (for each led) to the time since last flash, and if it is past a threshhold, it should turn the led on otherwise it should turn it off -- make a more extensive calibration tool, that completes all tests and fills in the eeprom accordingly -- report -\ No newline at end of file diff --git a/CS12020/robot/line/checklist b/CS12020/robot/line/checklist @@ -1,24 +0,0 @@ -calibrate -======= -- [x] read white -- [x] read black - -main program -=========== -- [x] follow line -- [x] stop when object - - [x] yellow LED -- [x] avoidance - - [x] red LED -- [x] attraction - - [x] green LED -- [ ] EEPROM - - [x] LDR black values - - [x] LDR white values - - [ ] motor values -- [ ] Testing - - [ ] buttons - - [ ] LEDS - - [ ] LDRS - - [ ] Servo debug msgs - - [ ] function -\ No newline at end of file diff --git a/CS12020/robot/line/line.ino b/CS12020/robot/line/line.ino @@ -5,7 +5,7 @@ #include "reading.h" -#define DEBUG_OFF +#define DEBUG #ifdef DEBUG #define DBG(expr) expr; @@ -283,7 +283,7 @@ move(state *s) /* try to recover from not being on the line by moving in the prevDir only updated on large turns */ while (!s->mid) { - getState(); + s = getState(); stepTurn(s->prevDir,s); } } diff --git a/CS12020/robot/report/README.md b/CS12020/robot/report/README.md @@ -0,0 +1,8 @@ +Lucas standen (lus53) CS12020 report and code +============================================= + +This contains all code and the report for my program +- The code folder holds the code +- The writeup folder holds all the latex stuff +- writeup.pdf is the final document +- The calibrate folder contains a small program to load values into the EEPROM for the robot diff --git a/CS12020/robot/report/calibrate/calibrate.ino b/CS12020/robot/report/calibrate/calibrate.ino @@ -0,0 +1,33 @@ +#include <EEPROM.h> +#include <stdint.h> +#include "helper.h" + +void +setup() { + Serial.begin(9600); + + /* black calibration */ + SEprintf("expecting to be on black\n"); + delay(3000); + + /* repeatedly changes calibration values for each ldr until returning consistant data */ + while (isLDRBright(LDRA)) LDRA_SWITCH++; + while (isLDRBright(LDRB)) LDRB_SWITCH++; + while (isLDRBright(LDRC)) LDRC_SWITCH++; + + SEprintf("%d, %d, %d\n\n", LDRA_SWITCH + 100, LDRB_SWITCH + 100, LDRC_SWITCH + 100); + + EEPROM.put(6, LDRA_SWITCH + 100); + EEPROM.put(10, LDRB_SWITCH + 100); + EEPROM.put(14, LDRB_SWITCH + 100); + + /* setting robots values, my robots values for fwd motion were the same on both servos so i just use SPEED, other robots will need different values */ + EEPROM.put(0, (uint8_t)SERVOA_ZERO); + EEPROM.put(1, (uint8_t)SPEEDA); + EEPROM.put(2, (uint8_t)SERVOB_ZERO); + EEPROM.put(3, (uint8_t)SPEEDB); +} + +void +loop() { +} diff --git a/CS12020/robot/report/calibrate/helper.h b/CS12020/robot/report/calibrate/helper.h @@ -0,0 +1,127 @@ +#ifndef __HELPER_H_ +#define __HELPER_H_ +#include <Servo.h> +#include <stdint.h> + +#define GREEN_LED 7 +#define YELLOW_LED 12 +#define RED_LED 13 + +#define BUTTONA 4 +#define BUTTONB 2 + +#define SERVOA_PIN 6 +#define SERVOB_PIN 5 + +#define LDRA A2 +#define LDRB A1 +#define LDRC A0 + +uint8_t SERVOA_ZERO = 83; +uint8_t SERVOB_ZERO = 85; +uint8_t SPEEDA = 10; +uint8_t SPEEDB = 10; + +#define ANALOG_MAX 1023 + +#define IRTRANSMITTER 3 +#define IRRECEVIER 2 + +#define IRFREQ 38000 + +#define SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +uint16_t LDRA_SWITCH = 0; +uint16_t LDRB_SWITCH = 0; +uint16_t LDRC_SWITCH = 0; + +enum DIRECTIONS { + FWD = 1, + BWD = -1, + LFT = -2, + RGT = 2 +}; + +Servo servoA; +Servo servoB; + +void +setupRobot() { + Serial.begin(9600); + pinMode(GREEN_LED, OUTPUT); + pinMode(YELLOW_LED, OUTPUT); + pinMode(RED_LED, OUTPUT); + pinMode(BUTTONA, INPUT); + pinMode(BUTTONB, INPUT); + pinMode(SERVOA_PIN, OUTPUT); + pinMode(SERVOB_PIN, OUTPUT); + pinMode(LDRA, INPUT); + pinMode(LDRB, INPUT); + pinMode(LDRC, INPUT); + pinMode(IRTRANSMITTER, OUTPUT); + pinMode(IRRECEVIER, INPUT); + servoA.attach(SERVOA_PIN); + servoB.attach(SERVOB_PIN); + servoA.write(SERVOA_ZERO); + servoB.write(SERVOB_ZERO); +} + +void +SEprintf(const char *fmt, ...) { + char sbuf[256] = { 0 }; + va_list ap; + + va_start(ap, fmt); + vsnprintf(sbuf, 256, fmt, ap); + va_end(ap); + + Serial.print(sbuf); +} + + +bool +isObstacle() { + tone(IRTRANSMITTER, IRFREQ); + delay(5); + bool ret = (digitalRead(IRRECEVIER) == LOW); + noTone(IRTRANSMITTER); + return ret; +} + +bool +isLDRBright(int pin) { + int v = analogRead(pin); + switch (pin) { + case LDRA: + return (v > LDRA_SWITCH); + case LDRB: + return (v > LDRB_SWITCH); + case LDRC: + return (v > LDRC_SWITCH); + } +} + +void +setLEDs(int g, int y, int r) { + digitalWrite(GREEN_LED, g); + digitalWrite(YELLOW_LED, y); + digitalWrite(RED_LED, r); +} + +void +waitButton(int pin, int state) { + while (digitalRead(pin) != !state) ; + delay(50); + while (digitalRead(pin) != state) ; + delay(50); +} +bool +isButtonAPressed() { + return !digitalRead(BUTTONA); +} + +bool +isButtonBPressed() { + return !digitalRead(BUTTONB); +} +#endif diff --git a/CS12020/robot/report/code/code.ino b/CS12020/robot/report/code/code.ino @@ -0,0 +1,356 @@ +#include <EEPROM.h> +#include "helper.h" + +#define READING_INTERVAL 300 + +#include "reading.h" + +#define DEBUG + +#ifdef DEBUG + #define DBG(expr) expr; +#else + #define DBG(expr) +#endif + +#define FLASHTIME 256 / 2 +#define TESTREADINGS 10 + +typedef struct state { + bool left, mid, right; /* is on black */ + int rawleft, rawmid, rawright; /* raw readings from LDRS */ + enum DIRECTIONS prevDir; /* enum to store which direction we moved in last, used to error correct */ + long timeStamp; /* barcode timestamp, used to see how long it has been since we went over a barcode */ + long lastmove, lastturn; /* time the last stepmove call was made, used to turn the motor off when we stop calling the functions */ + long gstate, ystate, rstate; /*used to store the time values of the LEDs to turn them off when needed */ +} state; + +const char *colors[] = {"white", "black"}; + +/* these functions are only used for debugging, and do not need to take up memory when not in use */ +#ifdef DEBUG + void + testLDR() + { + SEprintf("Testing LDRS\n"); + for (int i = 0; i < 2; i++) { + SEprintf("Please turn on the lights, and place the robot on a %s surface (press button A to continue)\n", colors[i]); + waitButton(BUTTONA, HIGH); + reading r = readSensorsMean(TESTREADINGS); + if (strcmp(colors[i], "black") == 0) { + EEPROM.put(6, (uint16_t)r.left + 100); + EEPROM.put(10,(uint16_t)r.mid + 100); + EEPROM.put(14, (uint16_t)r.right + 100); + } else { + EEPROM.put(4, (uint16_t)r.left); + EEPROM.put(8,(uint16_t)r.mid); + EEPROM.put(12, (uint16_t)r.right); + } + + SEprintf("Test complete, please turn off the lights (press button A to continue)\n"); + waitButton(BUTTONA, HIGH); + readSensorsMean(TESTREADINGS); + } + SEprintf("finished testing LDRS, please check the values are expected for lighting conditions\n"); + } + + void + testButton(int pin) + { + SEprintf("Testing button on pin %d, please press it 10 times in a row\n", pin); + for (int i = 0; i < 10; i++) { + waitButton(pin, HIGH); + SEprintf("Button pressed\n"); + } + SEprintf("Button testing complete, 10 values read, if you did not press the button 10 times, this test failed\n"); + delay(3); + } + + void + testServo(Servo s, int speed, int zero) + { + SEprintf("Testing Servo on pin %d, it will spin for 10 seconds\n"); + s.write(zero + speed); + delay(10000); + s.write(zero); + SEprintf("Servo test complete, if the servo did not spin for 10 seconds, this test failed\n"); + } + + void + testIR() + { + int seen = 0; + + SEprintf("Testing IR sensor, please place an object infront of the IR sensor\n"); + delay(5000); + for (int i = 0; i < 10; i++) { + if (isObstacle()) { + SEprintf("Detected object\n"); + seen++; + } else + SEprintf("Did not detect object\n"); + + delay(1000); + } + if (seen < 8) + SEprintf("Only saw the object %d times, test failed\n", seen); + else + SEprintf("Saw the object %d times, test passed\n", seen); + } +#endif + +void +flash(int pin, state *s) +{ + /* user facing function to start flashing of the LEDs */ + switch (pin) { + case GREEN_LED: s->gstate = millis(); break; + case YELLOW_LED: s->ystate = millis(); break; + case RED_LED: s->rstate = millis(); break; + } +} + +void +stepFlash(int pin) +{ + /* we define this a static to preserve its value across function calls */ + static uint8_t flashCount; + + if (flashCount < FLASHTIME) + digitalWrite(pin, HIGH); + else if (flashCount > FLASHTIME / 2) + digitalWrite(pin, LOW); + + /* this value wraps which is by design, this will cause the flashing to reset */ + flashCount++; +} + +void +updateFlash(state *s) +{ + /* turn off the leds if they arent ment to be on anymore */ + if (millis() - s->gstate > FLASHTIME) { + s->gstate = 0; + digitalWrite(GREEN_LED, LOW); + } if (millis() - s->ystate > FLASHTIME) { + s->ystate = 0; + digitalWrite(YELLOW_LED, LOW); + } if (millis() - s->rstate > FLASHTIME) { + s->rstate = 0; + digitalWrite(RED_LED, LOW); + } + + /* turn on the leds if needed */ + if (s->gstate) stepFlash(GREEN_LED); + if (s->ystate) stepFlash(YELLOW_LED); + if (s->rstate) stepFlash(RED_LED); +} + +void +stepMove(int dir, state *s) +{ + /* move the robot fwd or bwd */ + servoA.write(SERVOA_ZERO + (SPEEDA * dir)); + servoB.write(SERVOB_ZERO - (SPEEDB * dir)); + s->lastmove = millis(); +} + +void +stepTurn(int dir, state *s) +{ + /* turn the robot lft or rgt */ + servoA.write(SERVOA_ZERO + (SPEEDA * dir)); + servoB.write(SERVOB_ZERO - (SPEEDB * -dir)); + s->lastturn = millis(); +} + +void +updateMotor(state *s) +{ + /* turns of the motor if we hacent called step move/turn recently */ + if (millis() - s->lastmove > 10 && millis() - s->lastturn) { + servoA.write(SERVOA_ZERO); + servoB.write(SERVOB_ZERO); + } +} + +state * +getState() +{ + static state s; + + /* update all of the robots LDR readings */ + s.left = !isLDRBright(LDRA); + s.mid = !isLDRBright(LDRB); + s.right = !isLDRBright(LDRC); + s.rawleft = analogRead(LDRA); + s.rawmid = analogRead(LDRB); + s.rawright = analogRead(LDRC); + + updateFlash(&s); + updateMotor(&s); + + + return &s; +} + +void +stepAvoidanceBehave(state *s) +{ + flash(RED_LED, s); + DBG({ + /* WARNING this causes the program to stutter when left on, + not too bad for debugging but very bad for general use! */ + SEprintf("Avoid: %d, %d, %d\n", s->rawleft, s->rawmid, s->rawright); + }); + + /* left is noticably larger */ + if (s->rawleft > s->rawright + 50) + stepTurn(RGT,s); + /* right is noticably larger */ + else if (s->rawright > s->rawleft + 50) + stepTurn(LFT,s); + else + stepMove(FWD, s); +} + + +void +stepAttractionBehave(state *s) +{ + flash(GREEN_LED, s); + DBG({ + SEprintf("Attract: %d, %d, %d\n", s->rawleft, s->rawmid, s->rawright); + }); + + /* left is noticably larger */ + if (s->rawleft > s->rawright + 50) + stepTurn(LFT,s); + /* right is noticably larger */ + else if (s->rawright > s->rawleft +50) + stepTurn(RGT,s); + else + stepMove(FWD, s); +} + +void +runBehave(void (*stepBehave)(state *)) +{ + while (true) { + state *s = getState(); + stepBehave(s); + } +} + +void +checkBarcode(state *s) +{ + long t = millis(); + if (s->timeStamp > 500) { + /* long barcode */ + if ((t - s->timeStamp) > 3000) + runBehave(&stepAvoidanceBehave); + + /* short barcode */ + else if ((t - s->timeStamp) > 800) + runBehave(&stepAttractionBehave); + } + s->timeStamp = t; +} + +void +move(state *s) +{ + /* small adjustments */ + if (s->right && s->mid) + stepTurn(RGT, s); + else if (s->left && s->mid) + stepTurn(LFT, s); + + /* large adjustments */ + else if (s->right) { + s->prevDir = RGT; + stepTurn(RGT, s); + } else if (s->left) { + s->prevDir = LFT; + stepTurn(LFT, s); + + /* drive straight, nominal */ + } else if (s->mid) { + for (int i = 0; i < 20; i++) + stepMove(FWD, s); + } else { + /* try to recover from not being on the line by moving in the prevDir + only updated on large turns */ + while (!s->mid) { + s = getState(); + stepTurn(s->prevDir,s); + } + } +} + +void +setup() +{ + /* let the user attach the battery, attaching the battery while code is running caused strange behavior */ + delay(3000); + setupRobot(); + + /* servo speed values */ + EEPROM.get(0, SERVOA_ZERO); + EEPROM.get(1, SPEEDA); + EEPROM.get(2, SERVOB_ZERO); + EEPROM.get(3, SPEEDB); + SEprintf("A zero: %d\nB zero: %d\nA speed: %d\nB speed: %d\n", SERVOA_ZERO, SERVOB_ZERO, SPEEDA, SPEEDB); + + /* because this is static, I can modify these values to set defaults */ + state *s = getState(); + s->prevDir = BWD; + s->timeStamp = 0; + + /* test all the sensors and motors */ + DBG({ + testLDR(); + delay(1000); + testButton(BUTTONA); + delay(1000); + testButton(BUTTONB); + delay(1000); + testServo(servoA, SPEEDA, SERVOA_ZERO); + delay(1000); + testServo(servoB, SPEEDB, SERVOB_ZERO); + delay(1000); + testIR(); + delay(1000); + SEprintf("finished testing\n"); + }); + + /* read the calibrated values that should be in EEPROM, these values will be made by the tests above program */ + EEPROM.get(6, LDRA_SWITCH); + EEPROM.get(10, LDRB_SWITCH); + EEPROM.get(14, LDRC_SWITCH); +} + +void +loop() +{ + state *s = getState(); + + /* do nothing while there is an obstacle */ + while (isObstacle()) { + flash(YELLOW_LED, s); + return; + } + + /* the robots on a barcode */ + if (s->right && s->left && s->mid) { + checkBarcode(s); + + /* run through the barcode */ + for (int i = 0; i < 20; i++) + stepMove(FWD, s); + } + + /* normal movement */ + else move(s); +} diff --git a/CS12020/robot/report/code/helper.h b/CS12020/robot/report/code/helper.h @@ -0,0 +1,130 @@ +#ifndef __HELPER_H_ +#define __HELPER_H_ +#include <Servo.h> +#include <stdint.h> + +#define GREEN_LED 7 +#define YELLOW_LED 12 +#define RED_LED 13 + +#define BUTTONA 4 +#define BUTTONB 2 + +#define SERVOA_PIN 6 +#define SERVOB_PIN 5 + +#define LDRA A2 +#define LDRB A1 +#define LDRC A0 + +uint8_t SERVOA_ZERO = 84; +uint8_t SERVOB_ZERO = 85; +uint8_t SPEEDA = 10; +uint8_t SPEEDB = 10; + +#define ANALOG_MAX 1023 + +#define IRTRANSMITTER 3 +#define IRRECEVIER 2 + +#define IRFREQ 38000 + +#define SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +uint16_t LDRA_SWITCH = 0; +uint16_t LDRB_SWITCH = 0; +uint16_t LDRC_SWITCH = 0; + +enum DIRECTIONS { + FWD = 1, + BWD = -1, + LFT = -2, + RGT = 2 +}; + +Servo servoA; +Servo servoB; + +void +setupRobot() { + Serial.begin(9600); + pinMode(GREEN_LED, OUTPUT); + pinMode(YELLOW_LED, OUTPUT); + pinMode(RED_LED, OUTPUT); + pinMode(BUTTONA, INPUT); + pinMode(BUTTONB, INPUT); + pinMode(SERVOA_PIN, OUTPUT); + pinMode(SERVOB_PIN, OUTPUT); + pinMode(LDRA, INPUT); + pinMode(LDRB, INPUT); + pinMode(LDRC, INPUT); + pinMode(IRTRANSMITTER, OUTPUT); + pinMode(IRRECEVIER, INPUT); + servoA.attach(SERVOA_PIN); + servoB.attach(SERVOB_PIN); + servoA.write(SERVOA_ZERO); + servoB.write(SERVOB_ZERO); +} + +void +SEprintf(const char *fmt, ...) { + char sbuf[256] = { 0 }; + va_list ap; + + va_start(ap, fmt); + vsnprintf(sbuf, 256, fmt, ap); + va_end(ap); + + Serial.print(sbuf); +} + + +bool +isObstacle() { + int readings = 0; + tone(IRTRANSMITTER, IRFREQ); + delay(5); + for (int i = 0; i < 100; i++) { + if (digitalRead(IRRECEVIER) == LOW) readings++; + } + noTone(IRTRANSMITTER); + return (readings >= 80); +} + +bool +isLDRBright(int pin) { + int v = analogRead(pin); + switch (pin) { + case LDRA: + return (v > LDRA_SWITCH); + case LDRB: + return (v > LDRB_SWITCH); + case LDRC: + return (v > LDRC_SWITCH); + } +} + +void +setLEDs(int g, int y, int r) { + digitalWrite(GREEN_LED, g); + digitalWrite(YELLOW_LED, y); + digitalWrite(RED_LED, r); +} + +void +waitButton(int pin, int state) { + while (digitalRead(pin) != !state) ; + delay(50); + while (digitalRead(pin) != state) ; + delay(50); +} +bool +isButtonAPressed() { + return !digitalRead(BUTTONA); +} + +bool +isButtonBPressed() { + return !digitalRead(BUTTONB); +} +#endif diff --git a/CS12020/robot/report/code/reading.h b/CS12020/robot/report/code/reading.h @@ -0,0 +1,36 @@ +#include "helper.h" + +/* READING_INTERVAL should be declared by the caller program before this code is included */ + +typedef struct reading { + unsigned int left, mid, right; +} reading; + +reading +readSensors() +{ + return (reading){analogRead(LDRA), analogRead(LDRB), analogRead(LDRC)}; +} + +reading +readSensorsMean(int readings) +{ + reading r; + unsigned int left = 0, mid = 0, right = 0; + + for (int i = 0; i < readings; i++) { + r = readSensors(); + left += r.left; + mid += r.mid; + right += r.right; + SEprintf("single:%d,%d,%d\n", r.left, r.mid, r.right); + delay(READING_INTERVAL); + } + + left /= readings; + mid /= readings; + right /= readings; + SEprintf("mean:%u,%u,%u\n", left, mid, right); + r = (reading){left, mid, right}; + return r; +} diff --git a/CS12020/robot/report/writeup/writeup.tex b/CS12020/robot/report/writeup/writeup.tex @@ -2,6 +2,7 @@ \usepackage{tabularx} \usepackage{geometry} +\usepackage{pgfplots} \usepackage{titling} \usepackage{titlesec} \usepackage[english]{babel} @@ -18,6 +19,8 @@ \setlength{\parindent}{0pt} \setlength{\parskip}{10pt} +\pgfplotsset{width=15cm,compat=1.9} + \author{Lucas Standen} \title{Robotics report} @@ -26,14 +29,12 @@ \maketitle \newpage \section{How does the robot work?} -I designed my system around the idea of repeatedly polling the sensors, and adjusting the motors based on the input, I made functions called \lstinline{stepMove} and \lstinline{stepTurn} which each moved or turned the robot a small amount. These function ran in a very short amount of time, allowing them to be called as often as possible, letting me read the sensors more often. This aloud me to make my robot react to changes, such as going off course or touching the barcode, a lot faster than if I halted the program while the robot moved. Thanks to this I didn't have to worry about distances or angles in my code, as I could tell the robot to keep moving until something happened. +I designed my system to repeatedly poll the sensors, and adjusting the motors based on the input, I made functions called \lstinline{stepMove} and \lstinline{stepTurn} which each moved or turned the robot a small amount. These function ran in a very short amount of time, allowing them to be called as often as possible, letting me read the sensors more often. This aloud me to make my robot react to changes, such as going off course or touching the barcode, a lot faster than if I halted the program while the robot moved. Thanks to this I didn't have to worry about distances or angles in my code, as I could tell the robot to keep moving until something happened. The sensors were polled by a function called \lstinline{getState}, which would be called as often as possible and updated the readings of the sensors in the robot, and updated the state of the motors depending on how long they had be turned on for. My step function worked by storing a time stamp of when they were called (using \lstinline{millis}), then every time the get state function was called, I could see if it had been over 19 milliseconds since the motors had been told to move, if it had been, I could turn them off. The reason I did this was to avoid the need to turn the motors off in my behaviour functions and line following functions, as having that in them, would have made them more complex than they needed to be. -These functions were used to create a line following function, 2 behaviour functions and a barcode detection function. They are all called from the Arduino's loop function, allowing them to be called as often as possible. - \subsection{What went well?} -I was very happy with the robots attraction and avoidance behaviours, they both worked very well, and were reliable. The LED state functioned as expected, flashing repeatedly, this worked in a similar way to the motors, checking when the last call was made to flash the LED, allowing the code to spend more time on the sensors readings than the state of the LED. +I was happy with the robots attraction and avoidance behaviours, they both worked very well, and were reliable. The LED state functioned as expected, flashing repeatedly, this worked in a similar way to the motors, checking when the last call was made to flash the LED, allowing the code to spend more time on the sensors readings than the state of the LED. I was also happy with my use of a state \lstinline{struct} to hold all of the robots internal values, thanks to this my robot used almost no global variables. I declared it as \lstinline{static} inside of the \lstinline{getState} function, this meant that I could reassign the state variable in my \lstinline{loop} function, while not losing data, such as LED flash state. @@ -43,112 +44,10 @@ A final thing that I was proud of in my code was my debug macros, I wrote a macr I found my robot can sometimes get stuck while reading barcodes, if it did not enter them straight on, it would think that it needs to turn. This was partially fixed by adding an error correction, that would try to detect when this happened and turn in the opposite direction, to line the robot up with the barcode. This did help, however it still got stuck sometimes. -The robots turning while in line following mode can be very rigid. My robot would stop moving forward when it needed to turn and would then allow itself to keep moving. This often led to over or under correction. This could sometimes cause the robot to jitter on the line trying to course correct. - -\subsection{Analysing the code} - -\noindent\begin{minipage}{\textwidth} -The following code shows the \lstinline{loop} function of my code, this is where the majority of my logic is. Thanks to the smaller helper functions that I made, this can be very simple to modify if needed. - -\begin{verbatim} -void -loop() -{ - state *s = getState(); - - /* do nothing while there is an obstacle */ - while (isObstacle()) { - flash(YELLOW_LED, s); - return; - } - - /* the robots on a barcode */ - if (s->right && s->left && s->mid) { - checkBarcode(s); - - /* run through the barcode */ - for (int i = 0; i < 20; i++) - stepMove(FWD, s); - } - - /* normal movement */ - else move(s); -} -\end{verbatim} -\end{minipage} - -\noindent\begin{minipage}{\textwidth} -The following code shows my \lstinline{getState} functions, and other helping functions, these all worked together to make the code simpler in the main body of the program, which keeps the code maintainable. - -\begin{verbatim} -typedef struct state { - bool left, mid, right; - int rawleft, rawmid, rawright; - enum DIRECTIONS prevDir; - long timeStamp; - long lastmove, lastturn; - long gstate, ystate, rstate; -} state; - -void -stepMove(int dir, state *s) -{ - /* move the robot fwd or bwd */ - servoA.write(SERVOA_ZERO + (SPEEDA * dir)); - servoB.write(SERVOB_ZERO - (SPEEDB * dir)); - s->lastmove = millis(); -} - -void -updateMotor(state *s) -{ - /* turns off the motor if we haven't called step - move/turn recently */ - if (millis() - s->lastmove > 10 && millis() - s->lastturn) { - servoA.write(SERVOA_ZERO); - servoB.write(SERVOB_ZERO); - } -} - -state * -getState() -{ - static state s; - - /* update all of the robots LDR readings */ - s.left = !isLDRBright(LDRA); - s.mid = !isLDRBright(LDRB); - s.right = !isLDRBright(LDRC); - s.rawleft = analogRead(LDRA); - s.rawmid = analogRead(LDRB); - s.rawright = analogRead(LDRC); - - updateFlash(&s); - updateMotor(&s); - - return &s; -} -\end{verbatim} -\end{minipage} - -\noindent\begin{minipage}{\textwidth} -The following code shows my \lstinline{DBG} macro, that I've used throughout my code to help me find errors, or which state my robot is in at any point. This macro can work for any number of expressions, as the intended use case is that you pass a scope into the macro like so \lstinline`DBG({Serial.println("hello!");});` - -\begin{verbatim} -#define DEBUG - -#ifdef DEBUG - #define DBG(expr) expr; -#else - #define DBG(expr) -#endif -\end{verbatim} -\end{minipage} - \section{Comparing my code to the design brief} -\subsection{Working with Light Dependant Resistors} \noindent\begin{minipage}{\textwidth} -When the robot starts up, and has been compiled with DEBUG enabled, it will start a testing program, this program will perform all tests on the LDRs. The code that does so is presented here. +\subsection{Working with Light Dependant Resistors} + When the robot starts up, and has been compiled with DEBUG enabled, it will start a testing program, this program will perform all tests on the LDRs. The code that does so is presented here. This code will read 10 values from each sensor and output the readings, it will also write the values to EEPROM. I think this code properly achieves the target of testing so I would say it is worth 70\% of the maximum mark for this section. \begin{verbatim} void @@ -156,22 +55,32 @@ testLDR() { SEprintf("Testing LDRS\n"); for (int i = 0; i < 2; i++) { - SEprintf("Please turn on the lights, \ - and place the robot on a %s surface \ - (press button A to continue)\n", colors[i]); + SEprintf("Please turn on the lights, and place the robot + on a %s surface (press button A to continue)\n", colors[i]); waitButton(BUTTONA, HIGH); - readSensorsMean(TESTREADINGS); + reading r = readSensorsMean(TESTREADINGS); + + if (strcmp(colors[i], "black") == 0) { + EEPROM.put(6, (uint16_t)r.left + 100); + EEPROM.put(10,(uint16_t)r.mid + 100); + EEPROM.put(14, (uint16_t)r.right + 100); + } else { + EEPROM.put(4, (uint16_t)r.left); + EEPROM.put(8,(uint16_t)r.mid); + EEPROM.put(12, (uint16_t)r.right); + } - SEprintf("Test complete, please turn off the \ - lights (press button A to continue)\n"); + SEprintf("Test complete, please turn off the lights + (press button A to continue)\n"); waitButton(BUTTONA, HIGH); readSensorsMean(TESTREADINGS); } - SEprintf("finished testing LDRS, please check the values \ - are expected for lighting conditions\n"); + SEprintf("finished testing LDRS, please check the values are expected + for lighting conditions\n"); } + \end{verbatim} \end{minipage} @@ -197,10 +106,10 @@ move(state *s) /* large adjustments */ else if (s->right) { - s->prevDir = LFT; + s->prevDir = RGT; stepTurn(RGT, s); } else if (s->left) { - s->prevDir = RGT; + s->prevDir = LFT; stepTurn(LFT, s); /* drive straight, nominal */ @@ -208,12 +117,10 @@ move(state *s) for (int i = 0; i < 20; i++) stepMove(FWD, s); } else { - /* try to recover from not being on the line by - moving in the prevDir only updated on large - turns */ - + /* try to recover from not being on the line by moving in the prevDir + only updated on large turns */ while (!s->mid) { - getState(); + s = getState(); stepTurn(s->prevDir,s); } } @@ -259,7 +166,6 @@ checkBarcode(state *s) s->timeStamp = t; } \end{verbatim} -This code makes use of function pointers, this is to avoid over indenting the code, code that has been indented to much can become hard to follow. \end{minipage} \noindent\begin{minipage}{\textwidth} @@ -316,9 +222,11 @@ runBehave(void (*stepBehave)(state *)) \end{verbatim} \end{minipage} -\subsection{Robot states} +For this section I believe my code was effective and defensive, I included debug statements, didn't rely on global variables and split my program into manageable functions. While testing I found it didn't always work perfectly on edge cases, like the robot coming onto the line from an angle, because of this I believe my code is worth 60\% of the total marks for this section. + \noindent\begin{minipage}{\textwidth} -The robots states were handled through a \lstinline{flash} function that could cause any one LED to flash at a given point. +\subsection{Robot states} + The robots states were handled through a \lstinline{flash} function that could cause any one LED to flash at a given point. The code was effective and worked nicely, however the \lstinline{flash} and \lstinline{getState} functions, have to be called very frequently to properly flash the LEDs. Due to this I believe this code is worth 65\% of the maximum mark for the section. \begin{verbatim} void @@ -371,27 +279,63 @@ updateFlash(state *s) \subsection{EEPROM} \noindent\begin{minipage}{\textwidth} -The EEPROM was fairly easy to implement, my code only relied on the black calibration value, along with the motor values. + My code only relied on the black calibration value, along with the motor values, so not all the values were read. I wrote a simple program called calibrate that could however fill all values, and perform simple calibration of the LDR's (although the \lstinline{testLDRs} function was more effective at finding proper values). I believe, as I achieved the brief of filling in all the values and reading them too, this code is worth 70\% of the available marks for this section. + \begin{center} + \textit{Code for reading values from EEPROM} + \end{center} \begin{verbatim} -void -setup() -{ - /* let the user attach the battery, attaching the battery while code is running caused strange behaviour */ - delay(3000); - setupRobot(); - - /* read the calibrated values that should be in EEPROM, these values will be made by the calibrate program */ - EEPROM.get(6, LDRA_SWITCH); - EEPROM.get(10, LDRB_SWITCH); - EEPROM.get(14, LDRC_SWITCH); - - /* servo speed values */ - EEPROM.get(0, SERVOA_ZERO); - EEPROM.get(1, SPEEDA); - EEPROM.get(2, SERVOB_ZERO); - EEPROM.get(3, SPEEDB); - .... +.... + +/* let the user attach the battery, attaching the battery + while code is running caused strange behaviour */ + +delay(3000); +setupRobot(); + +/* servo speed values */ +EEPROM.get(0, SERVOA_ZERO); +EEPROM.get(1, SPEEDA); +EEPROM.get(2, SERVOB_ZERO); +EEPROM.get(3, SPEEDB); + +.... + +/* read the calibrated values that should be in EEPROM, + these values will be made by the calibrate program */ + +EEPROM.get(6, LDRA_SWITCH); +EEPROM.get(10, LDRB_SWITCH); +EEPROM.get(14, LDRC_SWITCH); +.... +\end{verbatim} +\end{minipage} +\newpage +\noindent\begin{minipage}{\textwidth} + \begin{center} + \textit{Code for setting default values into EEPROM} + \end{center} +\begin{verbatim} +/* repeatedly changes calibration values for each ldr + until returning consistant data */ + +while (isLDRBright(LDRA)) LDRA_SWITCH++; +while (isLDRBright(LDRB)) LDRB_SWITCH++; +while (isLDRBright(LDRC)) LDRC_SWITCH++; + +SEprintf("%d, %d, %d\n\n", LDRA_SWITCH + 100, + LDRB_SWITCH + 100, LDRC_SWITCH + 100); + +EEPROM.put(6, LDRA_SWITCH + 100); +EEPROM.put(10, LDRB_SWITCH + 100); +EEPROM.put(14, LDRB_SWITCH + 100); + +/* setting robots values, my robots values for fwd motion, and turns */ + +EEPROM.put(0, (uint8_t)SERVOA_ZERO); +EEPROM.put(1, (uint8_t)SPEEDA); +EEPROM.put(2, (uint8_t)SERVOB_ZERO); +EEPROM.put(3, (uint8_t)SPEEDB); \end{verbatim} \end{minipage} @@ -449,8 +393,240 @@ setup() \hline \end{tabular} } + \caption{\centering{\textit{All tests were performed in a well lit room, using lines and barcodes provided, unless otherwise stated}}} \end{table} -While testing my robot, I found a few issues of note. For example, while testing the IR sensor, it was always seeing something, even when nothing was present in front of it. I found this was because it was pointing inwards slightly, after turning it to face outwards, it started working as expected. I also found issues while testing with the LDRs, my testing program was receiving somewhat random offsets. I realised this was because I had not assigned the starting values to 0, so I was reading memory garbage. +\begin{center} +\begin{tikzpicture} + \begin{axis}[ + title=Graph showing LDR readings on a white background with the room bright, + ymin=0, ymax=600, + ] + \addplot[ + color=blue, + mark=square, + ] + coordinates { + (0,223) + (1,226) + (2,225) + (3,215) + (4,233) + (5,270) + (6,304) + (7,321) + (8,323) + (9,308) + }; + \addplot[ + color=red, + mark=square, + ] + coordinates { + (0,182) + (1,187) + (2,189) + (3,183) + (4,191) + (5,218) + (6,243) + (7,256) + (8,259) + (9,248) + }; + \addplot[ + color=green, + mark=square, + ] + coordinates { + (0,214) + (1,219) + (2,220) + (3,217) + (4,228) + (5,255) + (6,278) + (7,290) + (8,290) + (9,292) + }; + \legend{LDR A, LDR B, LDR C} + \end{axis} +\end{tikzpicture} + +\begin{tikzpicture} + \begin{axis}[ + title=Graph showing LDR readings on a white background with the room dark, + ymin=0, ymax=50, + ] + \addplot[ + color=blue, + mark=square, + ] + coordinates { + (0,11) + (1,11) + (2,11) + (3,11) + (4,11) + (5,11) + (6,11) + (7,11) + (8,11) + (9,11) + }; + \addplot[ + color=red, + mark=square, + ] + coordinates { + (0,15) + (1,14) + (2,14) + (3,15) + (4,15) + (5,15) + (6,16) + (7,15) + (8,15) + (9,15) + }; + \addplot[ + color=green, + mark=square, + ] + coordinates { + (0,8) + (1,8) + (2,8) + (3,8) + (4,9) + (5,9) + (6,9) + (7,9) + (8,9) + (9,9) + }; + \legend{LDR A, LDR B, LDR C} + \end{axis} +\end{tikzpicture} + +\begin{tikzpicture} + \begin{axis}[ + title=Graph showing LDR readings on a black background with the room bright, + ymin=0, ymax=200, + ] + \addplot[ + color=blue, + mark=square, + ] + coordinates { + (0,110) + (1,108) + (2,102) + (3,101) + (4,105) + (5,112) + (6,118) + (7,119) + (8,114) + (9,104) + }; + \addplot[ + color=red, + mark=square, + ] + coordinates { + (0,77) + (1,89) + (2,86) + (3,86) + (4,93) + (5,99) + (6,104) + (7,105) + (8,99) + (9,90) + + }; + \addplot[ + color=green, + mark=square, + ] + coordinates { + (0,88) + (1,101) + (2,99) + (3,99) + (4,105) + (5,111) + (6,116) + (7,116) + (8,111) + (9,103) + }; + \legend{LDR A, LDR B, LDR C} + \end{axis} +\end{tikzpicture} + +\begin{tikzpicture} + \begin{axis}[ + title=Graph showing LDR readings on a black background with the room dark, + ymin=0, ymax=10, + ] + \addplot[ + color=blue, + mark=square, + ] + coordinates { + (0,1) + (1,1) + (2,1) + (3,1) + (4,1) + (5,1) + (6,1) + (7,1) + (8,1) + (9,1) + + }; + \addplot[ + color=red, + mark=square, + ] + coordinates { + (0,2) + (1,2) + (2,2) + (3,2) + (4,2) + (5,2) + (6,2) + (7,2) + (8,2) + (9,2) + }; + \addplot[ + color=green, + mark=square, + ] + coordinates { + (0,0) + (1,0) + (2,0) + (3,0) + (4,0) + (5,0) + (6,0) + (7,0) + (8,0) + (9,0) + }; + \legend{LDR A, LDR B, LDR C} + \end{axis} +\end{tikzpicture} +\end{center} + \end{document}