Main Week
Presentation
The group
The group is working from different location in Iceland so some preparation was done online in preparation prior to the main week. The final results can be seen in outcome.
Here are links to the individual documentation pages of group members:
The group met in Ísafjörður Monday April 7th. We document what each team member did on the following days.
The instructors
Both instructors from Fab Lab Ísafjörður (Þórarinn Bjartur Breiðfjörð and Svavar Konráðsson) were with us for the first two days and only Svavar for the rest of the week. Great instructors that did everything they could for us.
Frosti Gíslason is Bjartur's instructor. He would have wanted to be with us but was occupied. He was ready to assist if needed.
Task Assignments
Jóhannes
- Vertical axis drive design and manufacturing
- PCB schematic, routing, soldering
- Creating all connection cables with connectors
- Programming and debugging the microcontroller
- Reverse engineering of components for 3D assembly
- Cable routing and final assembly
- Final iteration of arm bracket
- Assembling of arm with sun
- Photography and video recording of the machine
Ólöf
- Collect components
- Participate in designing in Schematic and PCB editor
- Participate in milling the PCB board
- Participate in soldering components to the PCB board
- Burn a bootloader to the ATSAMD21E18A-A microcontroller
- Design base/enclosure in Fusion
- Cut out the base/enclosure in the laser cutter
- Write a storyline for video
- Design a title page for video
- Line up video clips and images
- Add text/information to video
- Design a presentation slide
BjarturL (Landscape)
- Make the landscape model from the heightmap
- Make it a Circle
- Make it Functional
- Started 3D Printing
- Done 3D Printing
- Documented
- Done.
Högni
Day 1 (Monday)
Main tasks:
- Collect components
- Design the PCB board
- Design vertical drive
- Finish designing the base in Fusion
First PCB board designed in Kicad
Jóhannes and Ólöf searched for components that we wanted to use in our machine and collected them all in one place.
Jóhannes had created Kicad files for our board and he designed most of it. He and Ólöf cooperated on it, but it was mostly done by Jóhannes.
Fusion files created - components + enclosure
Jóhannes created Fusion files and added components to use in the design. He had drawn some components himself (components where no 3D CAD file could be found online). Ólöf designed the base/enclosure prior to the main week, with assistance from Jóhannes.
Landscape model
BjarturL
Main Tasks:
- Download the heightmaps
- Combine two heigtmaps in QGIS
- Choose the Area
- Make Isafjörður be the center
Today, I started redoing the model. The previous version looked fine and would have worked, but we would have needed to drill holes into it. So, we decided I would remake the model and, in the process, ensure that Ísafjörður was in the center. To achieve this, I needed to combine two heightmaps. First, I downloaded the two maps and imported them into QGIS. Next, I used Claude to generate a script in QGIS to combine the two maps. Here is the script:
from qgis.core import (
QgsRasterLayer,
QgsRasterBandStats,
QgsRasterDataProvider,
QgsProject
)
from qgis.analysis import (
QgsRasterCalculator,
QgsRasterCalculatorEntry
)
# === Input paths ===
input_raster_a = r"C:\Users\Bjart\Documents\QGIS_ISA\1.tif"
input_raster_b = r"C:\Users\Bjart\Documents\QGIS_ISA\2.tif"
output_raster_a = r"C:\Users\Bjart\Documents\QGIS_ISA\1a.tif"
output_raster_b = r"C:\Users\Bjart\Documents\QGIS_ISA\2a.tif"
# === Load rasters ===
layer_a = QgsRasterLayer(input_raster_a, "RasterA")
layer_b = QgsRasterLayer(input_raster_b, "RasterB")
if not layer_a.isValid() or not layer_b.isValid():
raise Exception("One or both rasters could not be loaded.")
# === Get stats ===
def get_min_max(layer):
provider = layer.dataProvider()
stats = provider.bandStatistics(1, QgsRasterBandStats.All)
return stats.minimumValue, stats.maximumValue
min_a, max_a = get_min_max(layer_a)
min_b, max_b = get_min_max(layer_b)
# === Global min/max ===
global_min = min(min_a, min_b)
global_max = max(max_a, max_b)
# === Create raster calculator entries ===
def create_entry(layer, name="raster"):
entry = QgsRasterCalculatorEntry()
entry.ref = f"{name}@1"
entry.raster = layer
entry.bandNumber = 1
return entry
entry_a = create_entry(layer_a, "RasterA")
entry_b = create_entry(layer_b, "RasterB")
# === Normalize Raster A ===
expression_a = f"(\"RasterA@1\" - {global_min}) / ({global_max} - {global_min})"
calc_a = QgsRasterCalculator(
expression_a,
output_raster_a,
"GTiff",
layer_a.extent(),
layer_a.width(),
layer_a.height(),
[entry_a]
)
result_a = calc_a.processCalculation()
if result_a != 0:
raise Exception(f"Normalization of Raster A failed with code {result_a}")
# === Normalize Raster B ===
expression_b = f"(\"RasterB@1\" - {global_min}) / ({global_max} - {global_min})"
calc_b = QgsRasterCalculator(
expression_b,
output_raster_b,
"GTiff",
layer_b.extent(),
layer_b.width(),
layer_b.height(),
[entry_b]
)
result_b = calc_b.processCalculation()
if result_b != 0:
raise Exception(f"Normalization of Raster B failed with code {result_b}")
print("Global normalization complete!")
Then I reimported them, and now I had two heightmaps that matched each other. The rest was quite simple. I followed the instructions from the preparation page, and then I had a model of Ísafjörður.
Day 2 (Tuesday)
Main tasks:
- Finish drawings of all purchased components in Fusion
- 3D print dial knobs
- Finish KiCAD schematic
- Make PCB routing and production files
- Mill the PCB board
- Solder on the PCB board
- Design the top on the base and the holes for components
Fusion - top of enclosure + holes
Ólöf designed the top of the base and then she drew holes for components. This was all done with assistance from Jóhannes.
Schematic design of first board
Jóhannes finished the schematic drawing in KiCAD for the PCB that includes all the peripherals and components for our machine. This is a screenshot of the drawing.
Then he made the routing of the PCB traces and finished the design. First we ran into trouble because Ólöf and Jóhannes were trying to cooperate on a shared KiCAD drawing in Git. Because we weren't using the same version of KiCAD and not exactly the same library version, we had some issues to get it working.
The next issue was, that when updating the PCB from schematic only some few ratnest, but no footprints appeared. The issue was, that almost all objects were hidden for some unknown reason. See the screenshot below:
After having fixed that issue, the tracing was pretty straight forward, but we had to use some bridges and rearrange some pins for convenience.
Here is the result in KiCAD:
Kicad design files for first PCB board
And here are the design files:
Here you can see what the board looked like after milling:
Knobs designed
Jóhannes designed knobs. Here is one printed knob in front of the Fusion drawing on the screen. He printed four knobs in total (one spare).
First PCB board milled
Ólöf and Jóhannes milled the PCB board in the Roland Modela Mdx-20. We are using the ATSAMD21E18A-A microcontroller and the traces are very thin. Jóhannes had to tune the size of the pads down in the footprint editor and we had to adjust the settings before milling because it seemed as if the machine would not mill everything it should (some diagonal paths).
On this image you can see Jóhannes pointing where there should be paths to be milled, but they were missing.
This was fixed by telling Fab Modules that we were using smaller endmill than we were actually using. Then the program created all paths we were hoping for.
After this Jóhannes and Ólöf participated in soldering the board. Jóhannes did most of it, f.ex. the supertiny traces around the microcontroller.
Design of landscape model finished
BjarturL
Main Tasks:
- Finish the model
- Make it into a circle
- Started 3D printing
Today, I finished the model. I used Blender with a boolean modifier to shape it into a circle and added the necessary holes for the magnet and the motor connection. Then, I prepared the model for printing in PrusaSlicer and started printing it on the Ender 3.
Day 3 (Wednesday)
Main tasks:
- Print the components for the vertical axis drive
- Solder/Connect a USB-C to the PCB board
- Burn a bootloader to the PCB board
- Debug PCB board
- Add holes on the enclosure for input and output
- Add components to the enclosure
- Design text and images on base
Components for vertical axis drive
Today Jóhannes printed the components for the vertical axis drive and now this subassembly is finished.
The fit for the bearings was very good and everything fit, except for the bearing seat in the upper flange. There was a small mistake in the design and the seat was too shallow.
We could have printed a new upper flange, which takes about 4,5 h, but instead Jóhannes decided to try and fix the seat in the lathe. Because it is a very small lathe, the chuck couldn't hold the hexagon on the outside. He had to make a small adaptor plate and hold the flange by mounting it with screws to the adptor plate. That was easy and took about 30 min.
After lunch it was time to assemble the drive.
Here Jóhannes glued the Hall effect breakout board in place:
And here is what the other side looks like (with a HX-711 motor driver):
Gear printing
Spur gears with M 0,6 seem to be close to the lower limit of what a regular FDM printer with 0,4 mm nozzle can achieve.
Bootloader burned
Ólöf burned the bootloader to the microcontroller. Svavar Konráðsson showed how to use a bootloader for the ATSAMD21E18A-A, following the directions from here, which led us to this site about the Programmer SWD D11C here and to Arduino Sam tutorial here.
In the image below you can see how the main PCB board and the D11C-based programmer with SWD connector are connected in the process. Ólöf's first attempt did not work but Bjartur noticed that she had not chosen programmer in the process. When the programmer had been chosen, the bootloader was burned.
In the image below you can see written that the bootloader has been burned:
Programming and troubleshooting first PCB board
The next step was to program the microcomputer, but it did not respond as it should. Jóhannes tried to debug the board, but the debugging did not go well. We weren't able to get the I2C channel to work and had other intermitted issues with the board (not recognized as serial device, no response on serial monitor). Unfortunately, the only logic analyzer that we have in our Lab was not available during the whole machine week, because one of our instructors took it with him to a business trip. We made some modifications and tried to get help, but nothing seemed to solve the issues for us. We even tried replacing the microcontroller.
The later half of the day went into troubleshooting the USB connection and serial communication.
At one point we could program the board via SWD and receive an output on the serial monitor, but after that everything seemed to get worse. Our supervisor tried to assist, but in the end we had to give up - quite frustrated.
The status by the end of the day is a board that:
- isn't recognized as USB-device
- doesn't send any thing via USB
- seems to be still programmable via SWD
Gear printing
During the troubleshooting we found two potential issues, that we could fix by adding jumper wires and a capacitor for noise reduction close to the SAMD21 voltage supply. Will it help?
While others were working on debugging, Ólöf finished adding holes in right sizes for each input and output. Then she added components to the surface of the enclosure in Fusion; LCD screen, buttons and switches.
BjarturL
Main tasks
- Document
- Fab Academy Meeting
Today was the Fab Academy meeting so that took up most of the day. So the main thing i did today was document.
Day 4 (Thursday)
Main tasks:
- Continue debugging PCB board
- Finish design of enclosure
- Laser cut and rasterize enclosure
More debugging on first PCB board
This day started with continuing issues with USB and serial communication. When Jóhannes had tried sending the program to a development board and even the development board stopped responding, he started suspecting the code to be the issue.
That turned out to be right. He excluded the libaries from the code and now things started working. He started going through the peripherals testing them step by step:
- the potentiometers
- the end-switch and set-switch
- the motor drivers
The LCD display didn't work so he started looking at the SERCOM functions of the SAMD21 and how to define the right pins for I2C.
He found a page here that explains it, but struggled to understand the instructions.
However, even with trying all the possibilities and adjusting different setting we weren't able to get the I2C to work.
By the end of the day we started considering and discussing possibilites of using different microcontrollers and had several options, but eventually could narrow it down to one (possibly) feasable option that would be the AVR128DB32.
Our instructor Svavar started designing a breakout board for this microcontroller, while Jóhannes looked at the cutting files of the enclosure and made some fixes.
Design of decorations on enclosure
Ólöf used this day to finish adding information and decorations to the enclosure. She finds the math and programming behind the function of this machine so amazing and she wanted what was going on to appear on the surface of the enclosure. She designed one side with the formulas for calculating the zenith and azimuth angle of the sun and added an explanatory image of it.
On another side input and output were written. Ólöf made a pen drawing of the mountains that the sun hides behind, traced the drawing in Inkscape and used it on two sides of the enclosure.
Then Bjartur suggested that we would add a map of the Westfjords in Iceland and Jóhannes suggested that a pin would be added to the map, to show where Ísafjörður is.
We wrote the names of the group members and wrote Fab Lab Ísafjörður, where we realized this machine.
Links to images and information on enclosure
Here are links to all information and images used to decorate the enclosure:
Link to solar azimuth angle formula and calculator here. The formula was used on the design of the enclosure.
Link to solar zenith angle formula and calculator here. The formula was used on the design of the enclosure.
Link to explanatory image of zenith and azimuth angle used on enclosure here
Link to map of Westfjords here. The image was used on the design of the enclosure.
Lasercutting the enclosure
Ólöf had little time left to finish lasercutting the base, before leaving Ísafjörður and the last steps were taken in too much hurry. She made mistakes and had to leave before the lasercutting was done. Jóhannes finished the job the day after, when mistakes had been fixed. The top of the enclosure appeared as three lines in Inkscape when it was imported from a DXF file. Maybe this happened because the file was imported more than once. This meant that the laser did the cutting three times and it ruined the piece. Ólöf had also not noticed that the design was so big that she should have been more careful when setting the homepoint, so the design went a bit out of the boarders of the MDF plate.
When Ólöf used the Combine with the Cut command under it in Fusion, to make the tabs on the top create slots in the sides, it did not work as it should, resulting in a straight line at the top of two sides instead of a side with fingers. This would probably not have happened if she had turned off sketches when combining/cutting.
Then she exported a DXF for two sides with holes in it, but the holes were not in the DXF. That would probably not have happened if she had turned off other sketches than what she planned on using.
Jóhannes and Ólöf fixed these mistakes and Jóhannes finished lasercutting the rest.
Day 5 (Friday)
Lasercutting of enclosure finished
Jóhannes finished lasercutting what was left of the enclosure. Then he assembled the front panel + back panel and added rubber feet to the bottom.
Design and production of second PCB board
This morning we made a Rev B version of our board and tried some tips from our instructor and from the mattermost chat, but nothing got the I2C to work. We discussed other solutions, but because of the limited number of pins available, we were quite restricted to I2C and we need a display for our machine.
Because the I2C pins of our board were accidentally damaged and we were still hoping to get it running with the help from Mattermost and our instructors, Jóhannes made a new board, with some improvements as two 4,9k pulldown resistors to SDA and SCL (as by recommendation of our instructor).
Debugging of second PCB board
Then we did some more testing and debugging, but without any major success. We tried defining the pins Wire.begin(8,9)
and tried the Softwire.h
library as well.
Below is an image of the different boards we tried (after desoldering some components we reused).
A new PCB board designed with AVR128DB32
During lunchtime we tested a development board with AVR128DB32 chip and it worked with the I2C display. Therefore, we decided to redesign the PCB and produce it with the new architecture. Jóhannes did the redesign and production of the new board. It took two attempts to mill it, because there was a dull bit in the machine when it was milled the first time. The second attempt went well. This was ready in the evening and finally something started to work.
Below is a screenshot of the new PCB.
Design files for PCB board
And here are the design files:
The next step was to solder and program the new PCB and what a relief when the screen and some other peripherals worked finally!
We used the DXdCore for determining the pinout.
Jóhannes used the rest of the day to develop different loops of the program and testing and debugging the inputs and outputs.
Search for video editor - Movavi tested
Ólöf used this day to search for and try out a Video editor. She began with Movavi.
Free edition of Movavi
She took some clips and created a short video with them. The Movavi app works well but the watermark in the free edition is quite big. This was a good practice and it gave some idea on which shots we would want to use in the final video.
Here you can see the main functions in Movavi (done in Canva by Ólöf):
Day 6 (Saturday)
Storyline and title slide for video
Ólöf used this day to write a storyline for the video. Then Jóhannes added to it. Ólöf designed a title slide for the beginning of the video. This design was done in Canva.
Programming and testing our second PCB - homing procedure for azimuth axis
Jóhannes spent most of this day programming and testing our new PCB, looking into the settings and properties of the AVR128DB32 microcontroller. He wrote a program for the homing procedure for the azimuth axis. Then he tested it and debugged it.
Then he made improvements to the display and interface and tested all switches and the adjustment knobs.
Until this point he had been running the machine on USB power and had not connected the LED either.
Power supply issues
Machine not working properly
When he connected it to 12V DC he realized that the machine wasn't working properly on it. The motor was quite weak and had issues turning the model. Additionally, the display was very faint.
He found out that the voltage regulator he used in the schematic was from the list of the Fab Inventory (NCV1117ST50T3G) and the one he used was marked "5V 1A voltage regulator" and had the same footprint, but was a different type and had not the same pinout!
He had to solder new voltage regulators to the board and fix the board with some jumper wires, but still the problem persisted.
PWM control for the light
He decided to take a break from trying to solve the voltage regulator issue until the next day. Instead he continued looking at the PWM control for the light, which had issues. It was constantly on, but the brightness changed a little when adjusting the PWM output.
After some research he found out that the high-side switching with a P-Mosfet isn't a good idea, when the logic voltage and the source voltage aren't the same. This was however what he had drawn in the schematic.
The solution to this was to change the circuit to a low-side swithcing N-Mosfet.
After drawing the changes in KiCad he tweaked the traces and was to rerout the 5V and GND and accomodate the N-Mosfet in a suitable location.
With these changes the light could be adjusted using the analogWrite(pin,pwm);
command.
Jóhannes also added a small pin to the landscape model to point out where our Lab is located exactly.
Day 7 (Sunday)
Debugging continued
Ólöf used the time she had this day to document and add files to the repository.
At this point there were still problems with the board. The P-Mosfet track was transmitting 3.3V to the light, both when the PWM was turned on and off. Jóhannes was able to fix the P-Mosfet track by changing it to N-Mosfet. He also changed the numbers of voltage regulators from 2 to 1.
For some reason the voltage regulator was only giving 4.5V instead of 5V, which was not enough to drive the motors and the LCD screen. Jóhannes looked into this with Svavar. It seemed as if the diode was causing a huge voltage drop when power was forwarded through her. Jóhannes switched the diode with a Schottky diode but there was only a small change. Jóhannes learned, that there are special IC circuits, that work like an ideal diode and should be used for applications like this, but unfortunately this wasn't available in our Lab.
The next solution that Jóhannes tried out was to use a small SMD slide switch instead of a diode but unfortunately this switch could only handle 100 mA and that was far from being enough.
The next attempt was to use a 2-pin socket header with a jumper wire between. This solution worked very well! The voltage kept stability and there was no faint in the display. Well, no faint until the motors were used. The voltage regulators, that were fed with12V, began heating up and the copper traced could not dissipate the heat. By protection mechanism it started reducing the output power. Jóhannes could not find a suitable heat sink but he realized that he could run the motors of a powerful USB power supply. He decided to move the voltage regulator from the board and make a power cable with a barrel jack and a USB connector. This solution worked.
After solving these problems he organized the cables and placed the PCB along with the motor drives inside the enclosure and connected the power with other cables. The red tape indicates the orientation of the connectors.
Video of azemuth homing procedure
This video shows the homing procedure after turning on the machine. The landscape stops exactly when the magnet is over the hall effect sensor (at an azimuth of 180°).
Day 8 (Monday)
Ólöf kept on documenting.
Landscape model painted
Jóhannes took the landscape model and painted the sea blue.
Note
Jóhannes used water based acrylic paint and painted directly on the PLA, without using primer.
The picture shows how our artificial sun is casting shadows on the newly painted landscape model.
Code improved
Jóhannes also worked on the program and made some improvements to the code and fixed some small bugs. Among other changes, the azimuth driver motor can now be turned off when the position is reached to save energy and stop the coils from generating heat.
Day 9 (Tuesday)
Sun axis
Jóhannes and Högni met in the afternoon and they decided that Högni would need some support to finish the sun axis assembly. Jóhannes made changes to the 3D design of the bracket to accomodate the support wheels in the right locations and make sure, that they can rotate freely. He improved overall strength and rigidity of the bracket and added a mount for the end switch. Then all parts were printed from black PETG.
Day 10 (Wednesday)
Sun axis assembly
Jóhannes assembled the newly printed parts this morning and did all the manual machining involved, as cutting bolts into exact length, countersink holes and so on. When he was happy with the arm and the bracket he added thread locker to the bolts on the arm, fastened and connected the end switch and made a trigger for it. Then he connected the motor and attached the bracket to the bottom of the enclosure.
Here are some pictures of the progress:
The code for the machine
After lunch Jóhannes wrote the last procedures for the code and tested them on the machine. Now everything works smoothly.
Here is the program for the machine in it's full length:
/* FABACADEMY 2025 - Machine Week (12)
* FabLab Ísafjörður
*
* DAYS OF DARKNESS
*
* A realistic landscape / solar model of Skutulsfjörður
*
* by Jóhannes Andrésson, Bjartur Leó Hlynsson, Ólöf Hannesdóttir, Högni Friðriksson
*/
#include <SolarCalculator.h>
#include <TimeLib.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
// Define Analog Inputs for poteniometer and hall effect sensor
const int ai_knob1 = 14;
const int ai_knob2 = 15;
const int ai_knob3 = 16;
const int ai_hall = 18;
// Define Analog Output for PWM of light
const int ao_pwm = 13;
// Define stepper motor constants and variables
const int steps = 48; // internal steps in motor
const float azratio = 97/10; // spur gear ratio
const float elratio = 834/10; // spur gear ratio
const float azstepdeg = 360/(steps*azratio); // deg per step
const float elstepdeg = 360/(steps*elratio); // deg per step
int azstep = 1; // azimuth step counter
int elstep = 1; // elevation step counter
float azpos; // az absolute position in deg
float elpos; // el absolute position in deg
const int do_M1P1 = 11;
const int do_M1P2 = 10;
const int do_M1P3 = 9;
const int do_M1P4 = 8;
const int do_M2P1 = 7;
const int do_M2P2 = 6;
const int do_M2P3 = 5;
const int do_M2P4 = 4;
// Define Digital Inputs (switches)
const int di_set = 24;
const int di_end = 25;
// Define Variables for solar path
double lat = 66.07243;
double lon = -23.118711;
int time_zone = 0; //UTC offset
// Define Date and Time
int yr = 2025;
int dy = 25;
int mo = 3;
int hr = 15;
int mm = 0;
// Solar position
double az; //azimuth in degrees
double el; //elevation in degrees
// Define Status Variables
bool home_az = false; // True when homing is complete
bool home_el = false; // True when homing is complete
bool run_stat = false; // True when model is running
int spd = 10; // Speed for constant operation in percentage
// Define Variables for display refresh
int lcddy;
int lcdmo;
int lcdyr;
int lcdaz;
int lcdel;
// run zenith stepper motor either forward or backward
int runel(unsigned long duration, bool dir){
static unsigned long chrono = millis();
if (millis() - chrono < duration) return;
// check if desired elevation is reached with accuracy of 1 deg
if (abs(elpos - el)< 1) return;
chrono = millis();
// make step
switch (elstep){
case 1:
digitalWrite(do_M1P1, HIGH);
digitalWrite(do_M1P2, HIGH);
digitalWrite(do_M1P3, LOW);
digitalWrite(do_M1P4, LOW);
break;
case 2:
digitalWrite(do_M1P1, LOW);
digitalWrite(do_M1P2, HIGH);
digitalWrite(do_M1P3, HIGH);
digitalWrite(do_M1P4, LOW);
break;
case 3:
digitalWrite(do_M1P1, LOW);
digitalWrite(do_M1P2, LOW);
digitalWrite(do_M1P3, HIGH);
digitalWrite(do_M1P4, HIGH);
break;
case 4:
digitalWrite(do_M1P1, HIGH);
digitalWrite(do_M1P2, LOW);
digitalWrite(do_M1P3, LOW);
digitalWrite(do_M1P4, HIGH);
break;
}
//forward or reverse operation
if(dir == true){
elpos = elpos+elstepdeg;
if(elpos > 360)elpos=elpos-360;
if(elstep < 4){
elstep++;
}else{
elstep = 1;
}
}else{ //run motor cw
elpos = elpos-elstepdeg;
if(elpos < 0)elpos=elpos+360;
if(elstep < 2){
elstep=4;
}else{
elstep--;
}
}
}
// homing function for elevation stepper motor
int homeel(bool dir){
// make step
switch (elstep){
case 1:
digitalWrite(do_M1P1, HIGH);
digitalWrite(do_M1P2, HIGH);
digitalWrite(do_M1P3, LOW);
digitalWrite(do_M1P4, LOW);
break;
case 2:
digitalWrite(do_M1P1, LOW);
digitalWrite(do_M1P2, HIGH);
digitalWrite(do_M1P3, HIGH);
digitalWrite(do_M1P4, LOW);
break;
case 3:
digitalWrite(do_M1P1, LOW);
digitalWrite(do_M1P2, LOW);
digitalWrite(do_M1P3, HIGH);
digitalWrite(do_M1P4, HIGH);
break;
case 4:
digitalWrite(do_M1P1, HIGH);
digitalWrite(do_M1P2, LOW);
digitalWrite(do_M1P3, LOW);
digitalWrite(do_M1P4, HIGH);
break;
}
//forward or reverse operation
if(dir == true){
if(elstep < 4){
elstep++;
}else{
elstep = 1;
}
}else{ //run motor cw
if(elstep < 2){
elstep=4;
}else{
elstep--;
}
}
}
// run azimuth stepper motor either forward or backward
int runaz(unsigned long duration, bool dir){
static unsigned long chrono = millis();
if (millis() - chrono < duration) return;
// check if desired azimuth is reached with accuracy of 1 deg
if (abs(azpos - az)< 1) return;
chrono = millis();
// make step
switch (azstep){
case 1:
digitalWrite(do_M2P1, HIGH);
digitalWrite(do_M2P2, HIGH);
digitalWrite(do_M2P3, LOW);
digitalWrite(do_M2P4, LOW);
break;
case 2:
digitalWrite(do_M2P1, LOW);
digitalWrite(do_M2P2, HIGH);
digitalWrite(do_M2P3, HIGH);
digitalWrite(do_M2P4, LOW);
break;
case 3:
digitalWrite(do_M2P1, LOW);
digitalWrite(do_M2P2, LOW);
digitalWrite(do_M2P3, HIGH);
digitalWrite(do_M2P4, HIGH);
break;
case 4:
digitalWrite(do_M2P1, HIGH);
digitalWrite(do_M2P2, LOW);
digitalWrite(do_M2P3, LOW);
digitalWrite(do_M2P4, HIGH);
break;
}
//forward or reverse operation
if(dir == true){
azpos = azpos+azstepdeg;
if(azpos > 360)azpos=azpos-360;
if(azstep < 4){
azstep++;
}else{
azstep = 1;
}
}else{ //run motor cw
azpos = azpos-azstepdeg;
if(azpos < 0)azpos=azpos+360;
if(azstep < 2){
azstep=4;
}else{
azstep--;
}
}
}
// homing function for azimuth stepper motor
int homeaz(bool dir){
// make step
switch (azstep){
case 1:
digitalWrite(do_M2P1, HIGH);
digitalWrite(do_M2P2, HIGH);
digitalWrite(do_M2P3, LOW);
digitalWrite(do_M2P4, LOW);
break;
case 2:
digitalWrite(do_M2P1, LOW);
digitalWrite(do_M2P2, HIGH);
digitalWrite(do_M2P3, HIGH);
digitalWrite(do_M2P4, LOW);
break;
case 3:
digitalWrite(do_M2P1, LOW);
digitalWrite(do_M2P2, LOW);
digitalWrite(do_M2P3, HIGH);
digitalWrite(do_M2P4, HIGH);
break;
case 4:
digitalWrite(do_M2P1, HIGH);
digitalWrite(do_M2P2, LOW);
digitalWrite(do_M2P3, LOW);
digitalWrite(do_M2P4, HIGH);
break;
}
//forward or reverse operation
if(dir == true){
if(azstep < 4){
azstep++;
}else{
azstep = 1;
}
}else{ //run motor cw
if(azstep < 2){
azstep=4;
}else{
azstep--;
}
}
}
void readai_stop (unsigned long duration) {
static unsigned long chrono = millis();
if (millis() - chrono < duration) return;
chrono = millis();
dy = int(float(analogRead(ai_knob1)) / 1023 * 30 + 0.5)+1;
mo = int(float(analogRead(ai_knob2)) / 1023 * 11 + 0.5)+1;
hr = int(float(analogRead(ai_knob3)) / 1023 * 23);
mm = int((float(analogRead(ai_knob3)) / 1023 * 23 - hr) * 60 +0.5);
}
void setbutton(unsigned long duration) {
static unsigned long chrono = millis();
if (millis() - chrono < duration) return;
chrono = millis();
if(digitalRead(di_set) == LOW){
run_stat = !run_stat;
}
}
void updateoled (unsigned long duration){
static unsigned long chrono = millis();
if (millis() - chrono < duration) return;
chrono = millis();
// check if values have changed - if not, return
if (lcddy == dy && lcdmo == mo && lcdyr == yr && lcdaz == int(az+0.5) && lcdel == int(el+0.5)) return;
// update line 1
lcd.clear();
lcd.setCursor(0,0);
if(dy<10)lcd.print("0");
lcd.print(dy);
lcd.print(".");
if(mo<10)lcd.print("0");
lcd.print(mo);
lcd.print(".2025 ");
if(hr<10)lcd.print("0");
lcd.print(hr);
lcd.print(":");
if(mm<10)lcd.print("0");
lcd.print(mm);
//update line 2
if(int(az+0.5)>=100)lcd.setCursor(0,1);
if(int(az+0.5)< 100)lcd.setCursor(1,1);
if(int(az+0.5)< 10)lcd.setCursor(2,1);
lcd.print(int(az+0.5));
lcd.print(char(223));
if(int(el+0.5)>=10)lcd.setCursor(7,1);
if(int(el+0.5)< 10)lcd.setCursor(8,1);
if(int(el+0.5)< 0)lcd.setCursor(7,1);
if(int(el+0.5)< -9)lcd.setCursor(6,1);
lcd.print(int(el+0.5));
lcd.print(char(223));
lcddy = dy;
lcdmo = mo;
lcdyr = yr;
lcdaz = az;
lcdel = el;
}
void solarcalc (unsigned long duration){
static unsigned long chrono = millis();
if (millis() - chrono < duration) return;
chrono = millis();
//set new time according to poteniometer input
setTime(hr,0,0,dy,mo,yr);
time_t utc = now();
// Calculate solar position
calcHorizontalCoordinates(utc, lat, lon, az, el);
}
// Adjust brightnes of the sun by elevation starting from -14°
void sunpwm (unsigned long duration){
static unsigned long chrono = millis();
if (millis() - chrono < duration) return;
chrono = millis();
int bright;
bright = int(3.6563*el+71.2);
if(bright > 254) bright = 254;
if(bright < 0) bright = 0;
analogWrite(ao_pwm,bright);
}
// Turn of motor to save energy
void stopaz (unsigned long duration){
static unsigned long chrono = millis();
if (millis() - chrono < duration) return;
chrono = millis();
digitalWrite(do_M2P1, LOW);
digitalWrite(do_M2P2, LOW);
digitalWrite(do_M2P3, LOW);
digitalWrite(do_M2P4, LOW);
}
void setup() {
analogWrite(ao_pwm,124);
Wire.begin();
lcd.begin(16,2); // Set the LCD size (16 columns and 2 rows)
lcd.backlight(); // Turn on backlight
lcd.setCursor(0,0);
lcd.print(" FABACADEMY 25");
lcd.setCursor(0,1);
lcd.print("DAYS OF DARKNESS");
// set switches with internal pullup
pinMode(di_set, INPUT_PULLUP);
pinMode(di_end, INPUT_PULLUP);
delay(2000);
analogWrite(ao_pwm,0);
}
void loop() {
// check if homing needs to be done
if(home_az == false){
//Homing azimuth axis
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Homing azimuth");
int hall_last = analogRead(ai_hall);
lcd.setCursor(0,1);
lcd.print(hall_last);
// when close to magnet reverse a bit
while(analogRead(ai_hall) < 500){
homeaz(false);
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Homing azimuth");
lcd.setCursor(0,1);
lcd.print(analogRead(ai_hall));
delay(30);
// when close to magnet
}
// approach magnet fast and stop
while(analogRead(ai_hall) > 400){
homeaz(true);
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Homing azimuth");
lcd.setCursor(0,1);
lcd.print(analogRead(ai_hall));
delay(30);
}
// find minimum value for magnet - compare with value from step before
// run until minimum has passed
do{
hall_last = analogRead(ai_hall);
homeaz(true);
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Homing azimuth");
delay(500);
}while(hall_last > analogRead(18));
// back one step
homeaz(false);
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Homing azimuth");
lcd.setCursor(5,1);
lcd.print("DONE");
home_az = true;
azpos = 180; // Home position is with sun from the south
delay(2000);
}
if(home_el == false){
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Homing zenith");
lcd.setCursor(0,1);
lcd.print(digitalRead(di_end));
while(digitalRead(di_end)==HIGH){
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Homing zenith");
lcd.setCursor(0,1);
lcd.print(digitalRead(di_end));
homeel(false);
delay(30);
}
home_el = true;
elpos = 0; //End switch is at around 0° elevation
lcd.clear();
lcd.setCursor(1,0);
lcd.print("Homing zenith");
lcd.setCursor(5,1);
lcd.print("DONE");
delay(1000);
}
// Update solar position
solarcalc(100);
// Check if set button is pressed
setbutton(200);
// Check if model is running or stop
if(run_stat == true){
if(abs(azpos - az)> 0.8)runaz(30,true);
if(abs(elpos - el)> 0.8){
if(el<elpos && digitalRead(di_end == HIGH))runel(30,false);
if(el>elpos)runel(30,true);
}
//set brightness
sunpwm(1000);
// when position is reached, turn off
if (abs(azpos - az)< 1 && abs(elpos - el)<1)run_stat = false;
}else{
stopaz(400);
readai_stop(500);
}
// Display update
updateoled(200);
}
Day 11 (Thursday)
Video editing
Ólöf used this day to work on video editing for the final video presentation. We haven't taken the final shots but she wanted to prepare everything as well as she could by practicing making videos. she wanted to see how the storyline would function with the clips we had done. She also wanted to try out adding text to videos. The first videos she made were done in Movavi. When it came to adding text she felt like the free version was offering limited variety of texts/fonts. This is the Movavi interface:
Then she decided to check out if she could use the Cap Cut editor. She had tried to download it before, but couldn't do it for some reason. Now she found out that she could edit the videos online, so that problem was solved. Here you can see an overview of the interface of Cap Cut:
After editing Ólöf wanted to add text and then she ran into problems. she began by adding text to the first scene after the title page. She chose a big, catching font. Then she went on and added text to another scene. When she changed the font on that scene, the font and size also changed in the first scene. She tried out different things but nothing worked. Here you can see how the text changed in colour and size, from blue to white:
After giving this problem some thought, she decided to export the video from Cap Cut without captions. Then she imported it into Canva. She chose a blank template, imported the video and started working on editing. The video was imported as one continuous scene so when she added a textbox to one scene, it appeared on all scenes throughout the video. This meant that she had to split the video into scenes with the Split page option. Here you can see the interface in Canva and the tools used, f.ex. the Split page tool.
This was very hard to do in Canva, but easy to do in Movavi and Cap Cut. In Canva it was hard to see exactly where one scene ended and another one began, so this took some time. Then when Ólöf added text to the scenes it was confusing to see which scene she was adding the text to. As you can see in the images below, in the upper image it looks as if the text is added to the title page scene but in fact it was in the next scene and it shows when you click on play (the lower image).
Since Ólöf likes the text and font options in Canva, she will use this method when creating the final video, that is exporting a video from Cap Cut and importing it into Canva, splitting the video into scenes and then adding text.
Day 12 (Friday)
Last video and photoshots
Jóhannes took pictures and videos of the model both outside in the good weather and indoors to get a better impression of the shadows being cast by the landscape model.
Presentation video
Ólöf used the day for video editing. She changed her mind about using Canva for adding text and decided to use Cap Cut editor. The reason was that she had run into problems before with adding text to the video in Cap Cut but then found out that it was simpler than she thought. She found this out when she looked closer at settings in Cap Cut. See explanations here below (made in Canva by Ólöf).
Ólöf and Jóhannes collaborated in the last steps of making the video.
Day 13 (Saturday)
Presentation slide designed
Presentation slide
Ólöf designed the preparation slide in Canva.
Slide in grayscale
She also made a slide in grayscale to see if it would look better than the colourful slide, but the colourful one here above was used as a final presentation slide.
Files
C++ Code
Fusion files
Fixed back with hole for arm dxf
Fixed back with holes for USB-C, power and switch dxf
Files with faults in them
Fusion file with Ólöf's first attempt to design a base without assistance. It was done in preparation:
In the Back Left dxf file the mistake was probably that a wrong plane was chosen when projecting the sketch:
In these files the top side was straight where there should be fingers:
Backside with hole for arm dxf
Too many lines and export too big
This final file gets extra explanations. When the top was exported as a dxf file and imported into Inkscape, the outlines were exported as three lines! The inner lines were exported as two lines. You can see this in the image below where the lines were dragged to each side.
Notice one more thing; When we were fixing the mistakes that were made when producing the parts of the enclosure, suddenly all sizes changed. This happened in Ólöf's computer and the reason is most likely some settings that she accidently changed. She hasn't found the reason but will probably find out what happened. For this reason components that were exported from Fusion and imported from dxf to Inkscape appeared far too big in Inkscape. F.ex. the width of this top should have been around 340mm but suddenly it is imported as 16.600mm.
The dxf file:
The Inkscape file: