WeatherBot - a Motorised Weather Machine | 3D Printable, ESP32 & OpenWeatherMap

9.9K18735

Intro: WeatherBot - a Motorised Weather Machine | 3D Printable, ESP32 & OpenWeatherMap

How to build your own Weather Forecasting Theatre with ESP32


Meet WeatherBot, my WiFi connected weather forecasting machine. WeatherBot retrieves a local forecast from OpenWeatherMaps and then shows you tomorrows weather with a motorised mini diorama. It automatically updates every few hours (you can set your own update interval) and shows a hyper-local forecast based on longitude and latitude.

The additional 2.9" e-ink display provides a more precise insight into both todays and tomorrows weather patterns without the distraction and brightness of a more traditional display.

In the photos above you can see two that I built. The white one had its white parts 3D printed and the wooden parts laser cut on my Snapmaker from some 3mm Birch Plywood. Don't worry if you don't have access to a laser cutter, the golden and bronze effect one was entirely 3D printed. šŸ™‚

The WeatherBot is powered by single USB cable making it easy to connect wherever you want to see the weathers outlook. The mechanics are driven by four micro servos and Iā€™ve designed the body to be clockwork like whilst allowing you to see the inner workings.

Iā€™ll explain step by step how to make one of your own.

STEP 1: Project Video


As with all my projects I have made an accompanying detailed step-by-step video tutorial. If you would like to see the WeatherBot in action then take a look at the first minute or so of the video.

Don't worry, I will also explain every step for the project in the remainder of this Instructable. I would suggest referring both to this written guide and the video if you ever find yourself a little confused as to what you should be doing whilst assembling your own - though I will be trying my best to be as detailed as possible here for you.

STEP 2: BOM (Bill of Materials)

To make one of your own you will need a few items. Here is a list of what I used and where you can find it for yourself online:

Nuts, bolts and screws needed:

  • M2.5x14 Bolts (x12)
  • M2.5x8 Bolts (x7)
  • M2.5 Nuts (x10)

STEP 3: Let's Get Printing...

I have uploaded all the 3D printable models and laser cutting files on Prussa's website here: https://www.prusaprinters.org/prints/107501-weatherbot-3d-printable-weather-forecasting-theatr

I have also attached each model file to relevant steps throughout this Instructable. This way you can either download them as you go, or get a complete set from the link above.

The first parts which you will need to print are the:

  • Four small cogs (Small Cogs.stl)
  • The rear section of the main body assembly (Main Body - Back - WeatherBot.stl)

I have designed the parts so that none of the printable components in this project require supports whilst printing. There is some short bridging required (nothing too taxing for a printer) so if your slicer has an option to detect bridging I would enable this.

All of my parts have been printed in PLA plastic. You could also use PETG or ABS if you would prefer if it is what you have to hand already.

STEP 4: Assemble the Servos

For this step you will need:

  • The 3D printed small cogs
  • Four 360 degree continuous rotation servos

The 3D printed cogs are designed to push fit onto the toothed cog on the top of the servos. It may require some force so I turned the servo over and pushed it down into the cog on the table. If yours go on too easily and you think that they might run the risk of slipping later whilst turning then you could consider carefully applying some glue.

The servos themselves are then fitted into the 3D printed rear frame using their included screws. First pass each servos wire through the cutout in each pair of pillars (this will also naturally show you which way around to orientate the servo). The servo is secured using the screws from above, two for each servo.

After they have been secured the wires from the servos can be passed through the four holes in the bottom of the print as shown in the photo above.

STEP 5: Creating the Parts for the Main Axle

To create the central axle and scene discs we need the following parts:

  • Spacers (x3 various sizes supplied)
  • Large Cogs (x4 various sizes supplied)
  • Scene Discs (x4 different designs supplied)

The four large cogs and the three spacers which sit between the scene discs are 3D printed. I increased the infill of these parts from the usual 20% to around 30% to improve their strength.

For the scene discs themselves you have the option of laser cutting them or 3D printing them.

3D Printing the Scene Discs

If youā€™re also printing the scene discs themselves then I recommend changing the filament to a second colour 2.8mm into the print. This will show the details of the scene very well as I have done when switching from the Bronze effect PLA to the Gold effect PLA as shown in the photos above.

Laser Cutting and Engraving Scene Discs

Since I have a Snapamaker I swapped its toolhead so that I could then laser cut and etch the scene discs. The discs need to be 3mm thick so Iā€™m using some 3mm birch plywood. Iā€™ve attached the files for laser cutting and etching these parts (the SVG files). Please ensure that when setting up the job in your laser program that the discs are showing as being 160mm in diameter. In some programs they seem to import at a reduced size.

I really like the look of laser cut wood with the burnt marks along their perimeters.

STEP 6: Assembling the Main Axle

With these parts prepared we can assemble the main axle. Start by passing the four cogs through one another. Start with the one with the longest shaft, thread the second longest over this, followed by the third longest and then the shortest.Give them a spin and check they turn with ease - if not, use some sandpaper to improve the fit.

It is important that they rotate freely without too much friction. Some time spent here will save a lot of work later so only proceed once you're happy with the way they turn.

We will be using glue to assemble the remaining pieces so you will need to be careful you donā€™t accidentally get glue where it shouldnā€™t be or the mechanism wonā€™t work properly. Add a few drops of glue to the top ledge of the first disc support (which is part of the shortest cog) we can then slide the rain scene disc onto this axle and against the glue. Ensure that it sits horizontal and level as it drys. Its rotation does not matter.

Add the first spacer, the largest one. Apply a couple drips of glue to the gap at the top and the surface of the spacer before adding the cloud cover scene. Again ensure it is level.

Repeat the same with the next spacer and the wind speed disc, then follow this with the last spacer and temperature disc. You will need to allow this to fully cure before using it but check it works after about 30 seconds, if any glue has made its way to where it should not be you might still have time to resolve it before it fully cures. All being good you can set this aside and we can work on the front frame next.

STEP 7: Print the Front Section of the Main Body

For the following steps of the build youā€™ll need to print the main front part of the body 'Main Body - Front - WeatherBot.stl'.

This can be a particular long print and will use about 225g of filament so ensure you have plenty on your roll before you start the print. There is no need for supports but don't forget to turn on ay bridging detection features in your slicing software if it has any.

STEP 8: Prepare the Limit Switches

For this step you will need:

  • Limit switches (x4)
  • Electrical wire
  • Soldering iron

Whilst the front section of the main body prints on your printer (previous step) we can use the time to begin preparing the four limit switches.

We will solder some wires to the normally open and common legs. On mine this is marked with 'C' on the common leg and 'NO' on the normally open leg.

Solder a separate pair of 19cm long wires to the Common and Normally Open legs of two switches, and a pair each of 16cm wires to the same legs on the other two remaining switches.

STEP 9: Install the Limit Switches

For this step you will need the following:

  • The four limit switches we just soldered wires onto
  • M2.5x14 Bolts (x8)
  • M2.5 nuts (x8)

We are going to bolt these inside the main printed part. The two switches with the longer wires will be going on the left hand side of the print as seen from the front.

Feed the wires for each switch through the holes adjacent to their locations on each fin. Install each switch so that the roller is towards the top and the switch itself is on the front face of each fin.

We can then use a pair of M2.5 x 14mm bolts and nuts to secure them.

For easy access I also recommend threading the bolt from the outside and adding the nuts on the inside. This way we can tighten them later by inserting a screwdriver from either the front side or rear of the project. Donā€™t over tighten them, leave them slightly loose for now. We will also leave them at the bottom of their available travel in the slots, later we will adjust their heights when we tune the machine.

Once added we can flip the print upside down and continue threading the wires through their respective tunnels on the underside.

STEP 10: Preparing the PCB

For this step you'll need:

  • An ESP32
  • Some 90 Degree Pins
  • Projects PCB (Optional but makes everything much easier)

Iā€™ll be using the PCB I designed for the project as it makes everything much easier to assemble. You can order your own PCB directly from PCBWay (see the link in the Bill Of Materials at the top) and sometimes I have spares listed on my Etsy shop so it might be worth looking there: https://www.etsy.com/uk/shop/DIYMachines

I have included two wiring diagrams in the photos for this step as well. One showing you an overview of how the PCB is wired, which I'll also be explaining over the next few steps in this article. The second shows you how to the make the connections without a PCB if you wanted to go down that route.

So to assemble the PCB, we can start with the ESP32. This sits over the main area as marked out on the PCB with its USB board over the cutout so that we can connect a USB cable later. Gently push its legs through the PCB and then turn the PCB over and solder the legs in place on the reverse.

Next are the 90 degree pins which we will connect the servos and display to later, they need to be added to the 5 groups of oval pads around the edges of the board. Four groups of three (two at each end) and one strip of 8 along the edge.

STEP 11: Connecting the Switches to the PCB

We have four limit switches already installed on our WeatherBot and these need to be connected to the PCB in a certain order.

On the PCB itself you will see four pairs of soldering through holes, numbered Switch 1 through to Switch 4 (with their two through holes directly beneath the label). The switches themselves are numbered from the front of the machine towards the back (though they have nothing printed on them) with the switch closest to the front being number one and the switch closest to the rear of the machine being number four.

With each individual switch though, it does not matter which wire for that switch goes into which one of the two holes on the PCB.

Pass the wires through through the PCB and solder from the other side.

STEP 12: Connect the Display

For this step you will need:

  • Waveshare 2.9" E-ink display

Connect the included wiring harness to the Waveshare display and then thread the female Dupont style connectors though the tunnel to the left of where the display will be installed at the front of the WeatherBot.

Once the wires are though the tunnel they can be connected to the corresponding pins on the PCB. You can tell which goes where by looking at the labels on the display, following the coloured wire and then connecting it to the identically labelled pin on the PCB.

STEP 13: Mount the PCB

For this step you will need:

  • M2.5x8mm bolts (x3)

The PCB can then be secured in place using three M2.5x8mm bolts. It is fitted on top of the three stand-off pillars. Be careful when tucking away the wires which go to the display that you do not damage any of the pins.

The display is then simply pushed into place on the front of the project. It will be held down securely later on when we install the front cover. We won't be doing that yet until we have uploaded the code and verified all of our electronics work as expected. (You may need to be able to remove the display to check connection if it does not work as intended later)

STEP 14: Install the Main Axle

For this step you will need:

  • Main Axle we assembled earlier and set aside
  • 3D printed Axil Cap 'Axle Cap.stl'
  • M2.5x8mm bolts (x2)

Bring back the main axle so that we can fit it in place, align the four discs with their respective slots - these are made up by the fins protruding from the sides and base of the print (see the first photo).

You can then gently push/pull open the front frame so that the front of the shaft can fit into the recess at the front of the main print. Once it is in place check that the discs can turn freely. If they can, then the 3D printed axil cap is bolted into place with two m2.5 x 8mm bolts over the centre of the axle between the cogs and scene discs. One face of the axle cap is chamfered, this side should go against the matching chamfer on the axle so that it faces the scene discs.

STEP 15: Tune Switch Positioning

Now we have both the discs and switches in place we can begin to tune their positioning. Take one disc at a time and rotate the scene disc by hand so that one of the notches on its perimeter passes upwards against the roller. For example the front disc should be rotated clock wise.

Listen for the switch to see if it engages and disengages properly as the notch passes (it will make a quiet clicking noise).

If it does not you will likely need to adjust the distance the switch is from the disc:

  1. Slightly loosen the bolts holding it in place.
  2. Move the switch closer or further away.
  3. Retighten the bolts.
  4. Check again.

Keep adjusting it until the switch only disengages when it passes a notch.Ā Repeat this process for all four discs.

STEP 16: Connect the USB Cable

Whilst we still have easy access to the ESP32 we can connect the USB cable. We will use this to upload the code shortly and to power the project once we are finished.

Whilst I can still draw your attention to it easily, I also want to point out to you the Boot button on the ESP32. You will need to press this later when we are uploading our code. šŸ‘šŸ¼

STEP 17: Connecting the Servos

On our PCB I have included a little white mark next to the each set of three pins for the Servo connections. This is here to indicate which pin is the signal pin. You need to ensure that your servos signal wire connects to this pin.

On my Servos the signal wire is the orange wire. I have included the three most common servo wire colour combinations in the image attached and marked which wire is the signal wire for you. If yours does not match the most common three then take a look at the servos data sheet or turn to Google.

As with the limit switches, it matters which servos is connected to which of our servo connections on the PCB. The numbering matches that of the limit switches. So servo 1 should be turning the cog which rotates the scene disc being monitored by switch number one. Again, I have created a graphic above which shows you which servo is connected where on the PCB.

STEP 18: Join the Two Halves

For this step you will need:

  • M2.5x8mm bolts (x2)
  • M2.5 nuts (x2)
  • M2.5x14mm bolts (x4)

We will join the two halves of the main body together now. To do this carefully slide the mini cogs and servos around the larger ones whilst watching they donā€™t get caught. Itā€™s a bit like a physical puzzle. You should also keep an eye that the servo leads do not get caught up at the bottom of the print. There are no moving parts below the base so any excess wire can be tucked away using a screwdriver to push them into the space around the PCB as you bring the two halves together.

Use the two pairs of 8mm m2.5 bolts and nuts at the top on each side to hold the two printed halves together, and then four 14mm m2.5 bolts through the bottom of the enclosure.

STEP 19: Uploading the Code

As we will be gluing the display cover in place during the next step it will be good idea to upload the code and check the display works as intended first. Before you upload the code there are a few things you will need to do:

  • Install additional Board ManagerĀ 

So we can then

  • Install ESP32 boards
  • Install required libraries:
  • Arduino_JSON by Arduino
  • Adafruit GFX by Adafruit
  • ESP32Servo by Kevin Harrington and John K. Bennett
  • GxEPD2 by Jean-Marc Zingg
  • Get a free OpenWeatherMap API key and a location ID for where you would like to report the weather for.
  • Enter your own wifi credentials

All of this I have written out in detail on my website, DIYMachines.co.uk. I have done it this way so that there is a single place I can keep the software instructions up-to-date (for the benefit of this Instructable, Youtube, and many more sites) if additional features are added or corrections need to be made. šŸ™‚

Once you have finished following the programming steps on my site and the code has finished uploading the DIY Machines WeatherBot splash screen should show on the display. If it does than the display is connected properly and we can finish fitting its enclosure.

STEP 20: Finishing the Display

You have a choice of two different Display covers, either a 3D printable one or a laser cut one.

The file for the laser cut cover 'Display Cover.dxf' only contains the cutting pattern. You will need to add your own engraving for the words 'Today' and 'Tomorrow' as shown in the photos above.

The 3D printed display cover looks best with the filament being changed to an alternative colour at 2.8mm into the print - the same technique we used for the scene discs.

Whichever cover you choose to make, it is simply held in place with some super glue in the corners.

STEP 21: Self Calibration / Homing

We've now finished building the WetherBot. So how will you know it's working?

When it first powers on you will see the DIY Machines splash screen, at this moment in time the WeatherBot is attempting to connect to the WiFi network and retrieve a weather forecast from the OpenWeatherMap service.

The scene discs will also start to rotate one at a time. The machine is looking at each disc in turn and timing the duration between the notches on the perimeter of each disc as they pass the limit switch. It can use this information to figure out the orientation of each disc (thanks to the two closely spaced notches the machine is looking for).

Once this has been determined and the weather forecast successfully received the display will update itself to show the overview of both todays and tomorrows weather. The WeatherBot will also begin to rotate the scene discs to display todays weather forecast.


STEP 22: Thanks for Reading

Enjoy your WeatherBot, mine has already stopped me getting caught in the rain without a coat at least twice. šŸŒ§

I would love it if you wanted to support my projects, please consider subscribing to me on here and Youtube (https://www.youtube.com/diymachines). I also have aĀ PatreonĀ page where some very kind people are helping me to cover the cost of designing, documenting and sharing these projects. Please consider joining them in any way if you can.

Thanks for reading to the end, have a wonderful day and until next time. Ciao for now.

18 Comments

OK my "I made it" post keeps disappearing, so I'll post this down here too:

Such a cool, fun project. I made one for myself and one for my grandparents. I caught/added a couple things during the course of making it:

  • windSpeedBelowWhichToShowStillScene and windSpeedAboveWhichToShowVeryWindyScene are commented as being in mph when they are created. However weatherTodaysWindSpeed is not converted to mph for the comparison. It's converted to mph when it is shown on the display, but not for the if statement comparison.

  • Rufnubbins touched on this, but the notches for disk 4 are configured to have a limit switch on the same side as disks 1 and 3.

  • I added a timer to the while loop in moveSpecificedServo, so if for whatever reason the servo stops moving halfway through a scene change, the code isn't caught in an infinite while loop.
const float servoTimer = millis();
...
while (digitalRead(currentDiscSwitch) == 1 && (millis() - servoTimer < 20000)) {
  • I added the time of the weather report to the screen, as without it, you can't tell if the information is current or from last week. I added this:
weatherUpdateUnix = int(myObject["hourly"][0]["dt"]) + int(myObject["timezone_offset"]);
weatherUpdateUnix = weatherUpdateUnix % 86400;
weatherUpdateUnix = weatherUpdateUnix / 60;
weatherUpdateTimeHours = weatherUpdateUnix / 60;
weatherUpdateTimeMins = weatherUpdateUnix % 60;
  • I also changed this else statement, so it could try and reconnect to wifi:
 if (WiFi.status() == WL_CONNECTED) {
...
}
else {
Serial.println("Reconnecting to Wifi");
display.clearBuffer(); //I'm using a different display than the guide
display.setCursor(5, 5);
display.setTextSize(2);
display.println("NO WIFI");
display.display();
WiFi.disconnect();
WiFi.reconnect();
delay(5000);
while (WiFi.status() != WL_CONNECTED) {

Serial.print(".");
WiFi.disconnect();
WiFi.reconnect();
delay(5000);

}
}
  • I was having an issue with the weather update time drifting further and further into the hour (I have it update every hour) so i changed this:
if (WiFi.status() == WL_CONNECTED) {
lastTime = millis(); //put this right underneath the if statement
  • the code also didn't seem to take into account millis() wraparound so i added this:
if (millis() - lastTime < 0) lastTime = millis();
if (millis() - lastTime > timerDelay) {
  • I was also having some trouble getting the motors to reliably detect the homing notch. It occurred to me that the homing notch is only needed for the homing. It's not needed for standard scene transitions. So I added a delay to the moveSpecifiedServo function to ensure the motor would move out past the homing notch before looking for the notch. I changed the code to look like this:
 discServo.write(currentDiscRotationSpeed);// start the servo moving
delay(450); //added longer delay
while (digitalRead(currentDiscSwitch) == 1 && (millis() - servoTimer < 20000)) {
  • and also changed the showWeatherOnSceneDisks to only "see" 3 notches:
   if (*currentDiscPosition == 1 && *currentDesiredDiscPosition == 1) {
//do nothing
}
else if (*currentDiscPosition == 1 && *currentDesiredDiscPosition == 2) {
moveSpecifiedServo(d, 1);
}
else if (*currentDiscPosition == 1 && *currentDesiredDiscPosition == 3) {
moveSpecifiedServo(d, 2);
}
else if (*currentDiscPosition == 2 && *currentDesiredDiscPosition == 1) {
moveSpecifiedServo(d, 2);
}
else if (*currentDiscPosition == 2 && *currentDesiredDiscPosition == 2) {
//do nothing
}
else if (*currentDiscPosition == 2 && *currentDesiredDiscPosition == 3) {
moveSpecifiedServo(d, 1);
}
else if (*currentDiscPosition == 3 && *currentDesiredDiscPosition == 1) {
moveSpecifiedServo(d, 1);
}
else if (*currentDiscPosition == 3 && *currentDesiredDiscPosition == 2) {
moveSpecifiedServo(d, 2);
}
else if (*currentDiscPosition == 3 && *currentDesiredDiscPosition == 3) {
//do nothing
}
*currentDiscPosition = *currentDesiredDiscPosition;
}
  • I might have missed this in the guide, but I didnt know what the homed scenes were supposed to look like. I uploaded the code and the scenes homed, but I didnt know if they had homed correctly. I had to figure out myself that a properly homed scene would have the scene with the two notches showing. Maybe I missed that in the guide, but if not, then it would have been helpful to have an image showing what the scenes were supposed to look like homed.

  • Finally, I'm working on an adaptor part so I can replace the servos with normal DC motors, as the servos are very noisy and annoying.

Thanks again for posting and sharing all this. I enjoyed making it and was impressed with how neat and put-together everything was.

It is absolutely wonderful project. I love it and surely will make it. Thanks for the very detailed instructions as well. One question though. As far as I know it is not recommended to connect directly that many servos and such to the esp32, usually the recommendation to use an external power source. Although I like the idea to use a common power source for everything is it safe to do so? Thanks!

The servo power pins are not pulling from the ESP, they're just on the positive rail along with the 32. The only direct connection to the ESP GPIO is the signal wire.

I'm trying to figure out how to code this to read out in degrees Fahrenheit. Any ideas or suggestions? This is an excellent project that I want to build with my grandson! Thanks so much!

Yes, you can either call your API in imperial units and simply change the values on lines 32 and 33 to Fahrenheit, or, if your API insists on feeding you Celsius, you can convert those values to F using (Ā°C ƗĀ 9/5) + 32, so for example on lines 308 and 333:

display.print(weatherTodaysTempFeel * 9/5 + 32,1);


display.print(weatherTomorrowsTempFeel * 9/5 +32,1);

I decided to get after this project as the result of 2 things. The first is I'm finding a new fascination with microcontrollers and am looking for neat projects to work with them. The second is the fact that my wife asks me all the time, "What's it like outside? Do I need my big coat? Jacket? Umbrella?" etc., and what better way to deal with repetitive tasks than automation? Throw in a cute little diorama and some servos, and I'm sold.

After a couple of months of collecting parts and having things 3d printed, I've completed it, though many modifications had to be made. I'm going to share my experiences here, in the hopes that they'll be useful to someone else attempting to tackle this project.


All references to the code in this comment are based off of the code from the github page (https://github.com/DIY-Machines/WeatherBot/blob/main/WeatherBot/WeatherBot.ino) as of 4 Feb 2024.


First, the ESP32 linked on Amazon is not the one used in the project. I had the PCB printed from JLCPCB (total of $3.50, which included shipping for 5 units), and the linked controller doesn't fit the PCB. I'm also bad at pinouts still so the linked controller I struggled with to test the display when I got it. I ordered these instead:

https://www.amazon.com/ESP-WROOM-32-Development-Microcontroller-Integrated-Compatible/dp/B08D5ZD528/

Which seem to be the units used in the original project. The AliExpress link also appears to be dead.



After collecting the requisite parts, I immediately ran into some issues with the code. I was getting compilation errors related to lines 200 and 213. Apparently the variables "weatherTodaysDescription" and "weatherTomorrowsDescription", though being declared as strings, had an ambiguous type and the IDE got the Big Mad about it. After some fiddling around, I learned about the JSON.stringify method (which is NOT in an obvious place in the library documentation), which allowed me to move the JSON data to the string variables successfully.

In addition, OpenWeather wants you to use their v3.0 API, and to do that they require a credit card, even to use their free tier, in case you make too many calls. I wasn't willing to give them my card, and the v2.5 API didn't give me the data I wanted or needed, so after looking about a bit I settled on the AccuWeather API. This necessitated a change in how the JSON data is stored in the variables. I also had to update my server url:

http://dataservice.accuweather.com/forecasts/v1/daily/5day/<your_location_ID_for_AccuWeather>?apikey=<your_api_key>&details=true

(the <> symbols should be removed for your URL)

The updated code looks like:

 // Storing todays weather
weatherTodaysDescription = JSON.stringify(myObject["DailyForecasts"][0]["Day"]["ShortPhrase"]);
weatherTodaysTempFeel = myObject["DailyForecasts"][0]["RealFeelTemperature"]["Maximum"]["Value"];
weatherTodaysPrecipitationProbability = myObject["DailyForecasts"][0]["Day"]["PrecipitationProbability"];
weatherTodaysWindSpeed = myObject["DailyForecasts"][0]["Day"]["Wind"]["Speed"]["Value"];
weatherTodaysCloudCover = myObject["DailyForecasts"][0]["CloudCover"];
weatherTodaysPrecipitationQty = myObject["DailyForecasts"][0]["Day"]["TotalLiquid"]["Value"];
Serial.print("Amount of rain today: ");
Serial.println(weatherTodaysPrecipitationQty);
Serial.print("Amount of clouds today: ");
Serial.println(weatherTodaysCloudCover);

// Storing tomorrows weather
weatherTomorrowsDescription = JSON.stringify(myObject["DailyForecasts"][1]["Day"]["ShortPhrase"]);
weatherTomorrowsTempFeel = myObject["DailyForecasts"][1]["RealFeelTemperature"]["Maximum"]["Value"];
weatherTomorrowsPrecipitationProbability = myObject["DailyForecasts"][1]["Day"]["PrecipitationProbability"];
weatherTomorrowsWindSpeed = myObject["DailyForecasts"][1]["Day"]["Wind"]["Speed"]["Value"];
weatherTomorrowsCloudCover = myObject["DailyForecasts"][1]["CloudCover"];
weatherTomorrowsPrecipitationQty = myObject["DailyForecasts"][1]["Day"]["TotalLiquid"]["Value"];

Note the JSON.stringify() in the weatherTodaysDescription and weatherTomorrowsDescription


Because I switched over to AccuWeather and live in the land of eagles and handguns, I had to modify the variables for when to show which disc:

// Temperature disc:
const int temperatureBelowWhichToShowColdScene = 45; // in degrees fahrenheit
const int temperatureAboveWhichToShowHotWeather = 80; // in degrees fahrenheit


// Wind Speed disc:
const int windSpeedBelowWhichToShowStillScene = 10; // in mph
const int windSpeedAboveWhichToShowVeryWindyScene = 30; // in mph


// Cloud Cover disc:
const int cloudCoverBelowWhichToShowClearSky = 4; // in %
const int cloudCoverAboveWhichToShowVeryCloudySky = 70; // in %


// Precipitation disc:
const int rainfallBelowWhichToShowDrySky = .02; // in in of rainfall
const int rainfallAboveWhichToShowVeryWetSky = .05; // in in of rainfall

Having done that, I noticed that the displayed wind speed was incorrect. Some digging led me to lines 319 and 343, which had a multiplier of "* 2.2369" a quick google later and I figured out the multiplication is to convert from m/s to mph, but I'm already being fed mph, so the math has got to go:

display.print(weatherTodaysWindSpeed,0);


display.print(weatherTomorrowsWindSpeed,0);

I also noticed some really strange likelyhoods of precipitation. AccuWeather gives you whole number percentages, and there's MORE math on lines 314 and 348. As much as I want a 2400% chance of precipitation, that's not how reality works, so the new lines look like:

display.print((weatherTomorrowsPrecipitationProbability),0);


display.print((weatherTomorrowsPrecipitationProbability),0);


I decided I didn't need temperature accuracy to the tenths of a degree, especially since all I ever saw was a .0, so I modified the temperature variables to be int instead of double:

int weatherTodaysTempFeel;


int weatherTomorrowsTempFeel;


Having done all of this, I realized that the "ShortPhrase" description being fed by AccuWeather is often... not as short as one would maybe believe given the description. This caused the description of the weather to wrap fairly often, rendering the temperature unreadable. I decided I didn't need the prepended text, and so ommitted lines 303 and 326 ("Dress for " and "It'll be ") to gain more space. With the display cover, I didn't feel the necessity for complete sentences. However, I was getting the string wrapped in double quotes, which was just kind of annoying. To remedy this I tacked on an extra function at the end of the .ino:

String removeFirstLastCharacters(String input) {
// Check if the string has at least two characters
if (input.length() >= 2) {
// Use substring to get a portion of the string excluding the first and last characters
return input.substring(1, input.length() - 1);
} else {
// If the string has less than two characters, return an empty string
return "";
}
}

and had to do some string manipulation. To accomplish this I declared 2 new variables at the beginning:

String weatherTodaysString;
String weatherTomorrowsString;

and added 2 lines in the void loop dealing with storing the JSON data:

weatherTodaysDescription = JSON.stringify(myObject["DailyForecasts"][0]["Day"]["ShortPhrase"]);
weatherTodaysString = removeFirstLastCharacters(weatherTodaysDescription);


weatherTomorrowsDescription = JSON.stringify(myObject["DailyForecasts"][1]["Day"]["ShortPhrase"]);
weatherTomorrowsString = removeFirstLastCharacters(weatherTomorrowsDescription);

and then updated the display code (lines 304 and 327):

// --------- TODAYS WEATHER --------------
//first line of todays weather
display.setCursor(5, 20);
//display.print("Dress for ");
display.println(weatherTodaysString);


// --------- TOMORROWS WEATHER --------------
//first line of tomorrows weather
display.setCursor(5, 91);
//display.print("It'll be ");
display.println(weatherTomorrowsString);


Thus have I solved the compilation errors, metric/imperial unit issues, and the display issues!

Having done all of that, I realized there was an issue with disc 4 (precipitation). When the switch was in the notch, it was displaying half scenes. Further poking revealed that discs 1, 3, and 4 all have notches that align when rotated to match, but 2 is different. To test, I moved the switch for disc 4 to the same standoff that the switch for 3 is on, and used longer screws to attach both switches (they both contact their respective discs; photo attached). This helped, but zeroing is still a bit weird. I also changed the direction of the #4 servo on line 388 (change the - to a +, matching discs 1 and 3):

currentDiscRotationSpeed = 90 + servoSpeed;


The final issue was some weirdness with one of the servos (in this case my #3). The issue was that when it hit its switch, it didn't stop; it simply slowed down. After futzing around in the code with servo speeds, I remembered that sometimes one must trim their servos. I strongly recommend that this be mentioned in the instructions, as it caused my disc 3 to never show the correct scene. I used the following code to do this. While it was running, I used a screw driver to trim up the potentiometer on the back of the motor until it wasn't moving (a value of "90" should have the motor at a full stop):


#include <ESP32Servo.h>

Servo servo;
const int servoPin = 21;

void setup() {
servo.setPeriodHertz(50);
servo.attach(servoPin);
}

void loop() {
servo.write(90);
}

You can of course change the pin for each motor to trim them, or create 4 servo objects and run them all at the same time.


I originally tried to laser cut the discs from wood. If you decide to go this route, I highly recommend using high quality products, as 2 of the discs ended up warping and made the axle assembly completely non-functional.


Overall, the project was super fun, and I now have a robot that tells my wife through pictures and numbers if she should wear her jacket! Thanks so much for putting this together, I would never have been able to do this on my own from scratch.

Very nice modern take on the old mechanical barometer driven 2-state weather dioramas.
For those intending to make one of these without access to a laser cutter can you please include the temperature and cloud cover STL's, as they appear to be missing from this Instructable.
Hi redbeardtrev. thank you for pointing out that I had missed this two files. I've now uploaded them to step 5 with the others for 3D printing.
Wow. That is an absolutely wonderful project! Beautifully designed and SO cool using the various technologies to build and implement. I'm working on an openweather project right now and this just made me more motivated and gave me some ideas :)
I'm really glad it inspired you in some way. šŸ˜Š
There's something forever intriguing about inventions which measure the weather or time.
Wow! Since I donā€™t like plastic and a pain to deal with 3D printersā€¦.I am going to look into making this mainly wood using laser cutter
A fully wooden one would look amazing. If I had the time I would have also made one from just wood. If you do make your I would love to see a photo.
Wow, this is hands-down the coolest project I've ever seen on Instructables. I am *not* a creator like this - I fumble enough just knitting socks, but your instructions were so clear that for a few minutes in the middle, I actually fantasized about maybe trying it myself. (Sanity returned when you got to the code part.) I have a friend who will find this very tantalizing and I'm passing the URL on to him. Congrats!
Thank you, I'm glad you enjoyed it! You should totally have a go and you can always turn to your friend for help - or the other makers and myself - if you get a little swamped at the code part.
Thank you for sharing it.
Lewis