Categories
Arduino Project Project Ideas Raspberry Pi Pico RTOS Software Development

Projects in the Real World

Testing projects in real world situations can help uncover issues that we wish would have been discovered earlier. One such example was with my Speech Timer Project.

I traveled to Binghamton, NY so I could attend my family reunion and decided to arrive early to attend the Morning Knights Toastmasters meeting in-person. I brought along my Speech Timer to try it out at the meeting. Before the meeting even started, I ran into an issue. The clock connects to the internet to get the time to set the real time clock (RTC). I knew this but thought the clock would still operate as the RTC does not need to be set for the speech timer to work. The configuration is set for my home network so I knew it would not connect to the internet at the meeting location. I pressed the button on the remote to activate the timer function and nothing happened. I ended up tucking the clock back into my backpack. I’ll need to edit the code and run some tests to make absolutely certain that it will work when it cannot connect to the internet.

This lesson shows how important it is to test our projects in a variety of situations. I believe I have tested this scenario before, so I will need to make certain that I can recreate it with the current code base, then move on from there.

Categories
Miscellaneous Software Development

Accessibility and the Apple Vision Pro

I read an article today titled, “I Know What the Apple Vision Pro Is For” in the New York Magazine. It was very interesting seeing how the Apple Vision Pro (AVP) is making a difference in the lives of people with disabilities. I find it amazing that Apple was able to make the device so accessible but see why they were able to be successful. They still have some work to do as the article points out but they have done a great job with the device.

Apple was able to be successful because they have developers on the team, who themselves have disabilities. This provides a few advantages for producing something that is accessible. First off, the developers have first hand knowledge of what will work and what will not. It is like having the business owner writing code or making design choices. Secondly is developers, who do not have disabilities can get near instant feedback.

I have not had the opportunity to work with someone who has a disability. I have worked with one individual many years ago, who I believe had oculocutaneous albinism, just as Maxine Collard, in the story. I recall that they had a jewelers loop and would get right up to the Cathode Ray Tube (CRT) monitor to see the screen. I guess that made the article a bit more special to me as I saw their struggles first hand and can imagine Maxine Collard’s interactions with the computer and other challenges she faces.

Getting near instant feedback is important to writing better code and really for doing anything well. I recall the first time that I worked on a project where the development team needed to write unit tests for the code that we were writing and coupled with DevOps and automated testing. It was a powerful combination. I was able to check in code and tell within a few minutes if the code was going to break the build or not. It was great when the code did not break the build but when it did, it was great to know it right away so the code was still fresh in my head. I did not need to get back into the mindset that I was in a week or two ago. Getting back in the mindset and remembering what you were doing when you wrote the code, why you made the design choices you did takes away valuable time that could be used to code the next chunk of functionality. I was so impressed by the time saving and the empowerment that near instant feedback gave me. I built a small device to show me the status of the builds.

Information Radiator
Information Radiator for Jenkins DevOps Builds

The near instant feedback gave some additional benefit to the project team as well. It did not eliminate defects, but it did result in fewer easy to find bugs. I did increase development time but the added benefits I feel outweighed the additional development effort and given enough time, would actually reduce development time as the developers would improve over time.

The immediate benefits is the code would be more robust, particularly around data input validation and business rule checks. Testers can write code to test these things but it takes a great deal of time to test various combinations of data inputs, edge cases, and requested business rule behaviors. Unit tests and the execution of those tests when a developer checks in code catches most of the issues that can arise from these cases.

Additionally as I started getting use to the instant feedback, I found that I was checking in code more frequently and was more comfortable in breaking code and user stories into smaller pieces. The reason for this I believe is two fold. Firstly there is gratification in seeing that code is checked in and passes tests as it is working as expected. Secondly, there is greater confidence that I change something that may impact some other functionality as I can be somewhat confident that if the code will break anything else, I will know it once the code is checked in.

While the article was on accessibility, I think there were some other things to learn as well. Getting near instant feedback is a great path to creating something that is reliable and works as intended. You can still develop code that no one uses, but it will be quality code. If you want code that users will find is useful, you need to get usability feedback as soon as possible from the community that will be using the software. Apple is a great example by including people with disabilities on their development teams.

Categories
Arduino Project Raspberry Pi Pico Software Development

Code Rewrite

I have started to rewrite the code based on the analysis of the parameters and timings. I have yet to look across the code for the various TPMS sensors to see if they use common code. I suspect that they do, but if they don’t it may make the approach impractical. I plan to investigate loading the parameters and timing values from files on the SD Card as well. I think the file approach may lead to a more sustainable approach.

The code rewrite starts with a structure, CC1101_Parameters, to hold the timing parameters and CC1101_Settings, to hold the settings for the CC1101 radio. The default values specified in the structures are the most common values for each property.

struct CC1101_Parameters {
  int expectedbitcount = 72;
  int expectedbytecount = 9;
  int expectedfifobytecount = 21;
  int syncbits = 16;
  int cdwidth_min = 5500;
  int cdwidth_max = 11500;
  int shorttiming_min = 35;
  int shorttiming_nom = 50;
  int shorttiming_max = 79;
  int longtiming_min = 80;
  int longtiming_max = 120;
  int synctiming_min = 175;
  int synctiming_max = 1200;
  int endtiming_min = 0;
  int endtiming_max = 500;
};  // CC1101_Parameters

struct CC1101_Settings {
  byte cc1101_defval_iocfg2 = 0x0C;    // GDO2 Output Pin Configuration - Serial out (synchronous)
  byte cc1101_defval_iocfg1 = 0x2E;    // GDO1 Output Pin Configuration - not used
  byte cc1101_defval_iocfg0 = 0x0E;    // GDO0 Output Pin Configuration - Carrier Sense output
  byte cc1101_defval_fifothr = 0x0F;   // RX FIFO and TX FIFO Thresholds - 64 bytes in FIFO
  byte cc1101_defval_sync1 = 0xD5;     // Synchronization word, high byte  11010101 01001111
  byte cc1101_defval_sync0 = 0x4F;     // Synchronization word, low byte
  byte cc1101_defval_pktlen = 0x09;    // Packet Length
  byte cc1101_defval_pktctrl1 = 0x00;  // Packet Automation Control
  byte cc1101_defval_pktctrl0 = 0x12;  // Packet Automation Control - synchronous data
  byte cc1101_defval_addr = 0x00;      // Device Address
  byte cc1101_defval_channr = 0x00;    // Channel Number
  byte cc1101_defval_fsctrl1 = 0x0F;   // Frequency Synthesizer Control
  byte cc1101_defval_fsctrl0 = 0x00;   // Frequency Synthesizer Control
  byte cc1101_defval_freq2 = 0x10;     // Frequency Control Word, High Byte
  byte cc1101_defval_freq1 = 0xB0;     // Frequency Control Word, Middle Byte
  byte cc1101_defval_freq0 = 0x71;     // Frequency Control Word, Low Byte
  byte cc1101_defval_deviatn = 0x40;   // Modem Deviation Setting (+/-25.390625kHz)
  byte cc1101_defval_mdmcfg4 = 0x59;   // Modem Configuration (59 = data rate = 20kHz, RX bw 325kHz)
  byte cc1101_defval_mdmcfg3 = 0x93;   // Modem Configuration (now 93 = data rate = 20kHz)
  byte cc1101_defval_mdmcfg2 = 0x10;   // Modem Configuration (GFSK, No Sync or Manchester coding)
  byte cc1101_defval_mdmcfg1 = 0x21;   // Modem Configuration Channel spacing 100kHz
  byte cc1101_defval_mdmcfg0 = 0xF8;   // Modem Configuration
  byte cc1101_defval_agcctrl2 = 0x87;  // AGC Control
  byte cc1101_defval_agcctrl1 = 0x58;  // AGC Control
  byte cc1101_defval_agcctrl0 = 0x80;  // AGC Control
  byte cc1101_defval_mcsm2 = 0x07;     // Main Radio Control State Machine Configuration
  byte cc1101_defval_mcsm1 = 0x3C;     // Main Radio Control State Machine Configuration
  byte cc1101_defval_mcsm0 = 0x18;     // Main Radio Control State Machine Configuration
  byte cc1101_defval_foccfg = 0x16;    // Frequency Offset Compensation Configuration
  byte cc1101_defval_bscfg = 0x6C;     // Bit Synchronization Configuration
  byte cc1101_defval_worevt1 = 0x87;   // High Byte Event0 Timeout
  byte cc1101_defval_worevt0 = 0x6B;   // Low Byte Event0 Timeout
  byte cc1101_defval_worctrl = 0xFB;   // Wake On Radio Control
  byte cc1101_defval_frend1 = 0x56;    // Front End RX Configuration
  byte cc1101_defval_frend0 = 0x10;    // Front End TX Configuration
  byte cc1101_defval_fscal3 = 0xE9;    // Frequency Synthesizer Calibration
  byte cc1101_defval_fscal2 = 0x2A;    // Frequency Synthesizer Calibration
  byte cc1101_defval_fscal1 = 0x00;    // Frequency Synthesizer Calibration
  byte cc1101_defval_fscal0 = 0x1F;    // Frequency Synthesizer Calibration
  byte cc1101_defval_rcctrl1 = 0x41;   // RC Oscillator Configuration
  byte cc1101_defval_rcctrl0 = 0x00;   // RC Oscillator Configuration
  byte cc1101_defval_fstest = 0x59;    // Frequency Synthesizer Calibration Control
  byte cc1101_defval_ptest = 0x7F;     // Production Test
  byte cc1101_defval_agctest = 0x3F;   // AGC Test
  byte cc1101_defval_test2 = 0x81;     // Various Test Settings
  byte cc1101_defval_test1 = 0x35;     // Various Test Settings
  byte cc1101_defval_test0 = 0x09;     // Various Test Settings
};                                     // CC1101_Settings

The next bit of code makes use of switch case statements to set the values based on the selected configuration.

void cc1101_init_vars() {
  switch (carSettings.freq) {
    case TPMS_Frequencies::UK_433MHz:
      switch (carSettings.tpmsSensorType) {
        case TPMS_Sensors::Citroen:
          cc1101_parameters.expectedbitcount = 88;
          cc1101_parameters.expectedbytecount = 10;
          cc1101_parameters.cdwidth_min = 8000;

          break;
        case TPMS_Sensors::Dacia:
          cc1101_parameters.cdwidth_min = 7000;

          cc1101_settings.cc1101_defval_freq0 = 0x0C;
          cc1101_settings.cc1101_defval_deviatn = 0x41;
          break;
        case TPMS_Sensors::Ford:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 10000;
          break;
        case TPMS_Sensors::Hyundai_i35:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 9000;
          cc1101_parameters.synctiming_min = 140;
          cc1101_parameters.synctiming_max = 160;
          break;
// ...
        case TPMS_Sensors::Zoe:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_min = 7000;

          cc1101_settings.cc1101_defval_freq0 = 0x0C;
          cc1101_settings.cc1101_defval_deviatn = 0x41;
          break;
        default:
          break;
      }
      break;
    case TPMS_Frequencies::US_315MHz:
      switch (carSettings.tpmsSensorType) {
        case TPMS_Sensors::Citroen:
          // UK 433 MHz Only
          break;
        case TPMS_Sensors::Dacia:
          // UK 433 MHz Only
          break;
        case TPMS_Sensors::Ford:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 10000;

          cc1101_settings.cc1101_defval_freq2 = 0x0C;
          cc1101_settings.cc1101_defval_freq1 = 0x1D;
          cc1101_settings.cc1101_defval_freq0 = 0x57;
          break;
        case TPMS_Sensors::Hyundai_i35:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 9000;
          cc1101_parameters.synctiming_min = 140;
          cc1101_parameters.synctiming_max = 160;

          cc1101_settings.cc1101_defval_freq2 = 0x0C;
          cc1101_settings.cc1101_defval_freq1 = 0x1D;
          cc1101_settings.cc1101_defval_freq0 = 0x57;
          break;
// ...
        case TPMS_Sensors::Toyota_TRW_C070:
          cc1101_parameters.expectedbitcount = 64;
          cc1101_parameters.expectedbytecount = 8;
          cc1101_parameters.cdwidth_max = 9000;
          cc1101_parameters.synctiming_min = 140;
          cc1101_parameters.synctiming_max = 160;

          cc1101_settings.cc1101_defval_freq2 = 0x0C;
          cc1101_settings.cc1101_defval_freq1 = 0x1D;
          cc1101_settings.cc1101_defval_freq0 = 0x57;
          break;
        case TPMS_Sensors::TruckSolar:
          // UK 433 MHz Only
          break;
        case TPMS_Sensors::Zoe:
          // UK 433 MHz Only
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

The next step is to determine if common code can be written for receiving the data from each brand of TPMS sensor.

Categories
Project

TPMS Tire Pressure Display – Code Redesign

Work has started with redesigning the original project code posted on hackster.io by JSMSolns. I have decided to use FreeRTOS, Real Time Operating System, to make the code more responsive to button presses. This may limit the supported microcontrollers to RP2040 and ESP32 microcontrollers. Limiting to these microcontrollers does allows for a larger code size if necessary and provides the potential added benefit to support Wi-Fi and Bluetooth in the future to provide an additional method for configuration and monitoring.

Here is an initial list of items/features that need to or may be developed.

  • Support for different display types
    May start with one display type then move to others.
  • Support SD Card
    • Load Configuration
    • Save edited configuration
    • Load sensor parameters
  • Alarm function
  • Switches and menu system
    • Turn alarms on or off
    • Move tire position
    • Assign sensor to a position
    • Setup new vehicle
  • Support multiple vehicles
    Allow switching the device to multiple vehicles without losing previous vehicle configuration.
  • Listen for updates
  • Update display with information
  • View received sensors

I’m currently looking at options for showing the status of the project such as Jira, Trello, or GitHub Issues. It would be good to have something to keep me on track and be able to communicate with others. Each option has its own limitations so I’ll need to determine how to work through them or drop some requirements that I have in mind.

Please let me know if you have any interest in the project or would like to help with moving it forward. I would like to have some other C++ developers to bounce ideas around.

Categories
Project

TPMS Project – Analysis for Adding Flexibility

The Arduino TPMS Tyre Pressure Display project posted on hackster.io is locked into the vehicle and sensor type at compile time. This does not make it easy to setup for a new vehicle. Additionally the sensor ids and positions cannot be changed after the code has been compiled. If you rotate the tires of your vehicle, you need to edit and recompile the code to change the positions. While these restrictions reduce code size, they do not make it easy to change anything unless you want to recompile the code.

The hard coding of values is fine for most people creating this project, but it would not be good if you want to build it for a friend or relative. Once the tires are rotated or a sensor is changed, it would not be useful to them any longer. If they live not too far, that may not be an issue as the person who built it could recompile the code with the necessary changes, but what if that individual was several hundred or thousands of miles away? The project may end up in the trash or gathering dust in the corner.

I have started looking at the differences in configuration and parameters for the vehicles and sensors supported in the code to identify similarities and differences. The goal is to keep the code as small as possible so that more microcontrollers may be supported but the main focus will be on supporting the Raspberry Pi Pico RP2040 microcontroller. Additionally an SD Card reader will be added to the code with the configuration stored in a file on the card. I’ve also added some buttons to my version, which will allow a menu to be added so it will be possible to switch between different sensor types, scan for TPMS sensors, and move or assign them to positions on the car. Below are some screenshots of the Excel Workbook that I’m using to do the analysis.

The next step is to determine the best way to move forward. Currently I’m considering determining what the most common configuration settings are, then adding code to make changes to the common configuration where there are differences. I have started down this path, but I see a few issues with this approach, primarily with maintaining the code. It would be necessary to keep the Excel workbook up to date with any code changes and only make changes in the proper case statements. While this would work fine, it could lead to bloated code especially if someone was unclear how to maintain the code and added all the parameters in a case statement. Another more flexible approach would be to have a configuration file for each sensor type but this could slow down the startup process but it is a viable option.

Categories
Arduino Project Raspberry Pi Pico

Comparing two Objects in C++

I’m in the process of modifying the Tire Pressure Monitoring System code to allow configuration information to be retrieved and saved to an SD card as well as making use of the three buttons that I added to my version. I had an issue with the display not initializing as expected. Upon further investigation, I realized that it was an issue with the comparison of two objects, with one being the SPI bus for the CC1101 radio module and the other being the SPI bus for the display. The code initializes the CC1101 SPI bus by default as the CC1101 SPI is required. The code then should initialize the display SPI bus if it is being used for the display and is not the same as the CC1101 SPI bus.

The SPI busses for the CC1101 and display are different, but the check passed on the display SPI bus initialization because the values are the same. I needed to look at the addresses to see if they are pointing to the same location. I used some code that I found on StackOverflow. When I pulled some of the code to look at the values and addresses, I was left scratching my head as it looked like they had the same value, the addresses they were pointing to were different, but the addresses of the pointers were the same. I scratched my head for a few minutes then it dawned on me that the example code was actually displaying the address of the function parameter and not the address of the pointer. Once I figured that out, I started gaining my sanity back. I decided to add a response to the post with some modified code to show what I was expecting. Hopefully it will help someone later if they run into the same issue.

The StackOverflow post is at https://stackoverflow.com/questions/32914298/print-value-and-address-of-pointer-defined-in-function. Below is the reply that I added.


I had noticed that the address of the pointer was the same for each call of the function given in the examples. It then dawned on me the reason was the address displayed was for the function parameter pointer, not the pointer being passed to the function.

There is nothing wrong with the examples and they work as intended. When I first viewed the code, I thought the intent was to display the address of the pointer that was passed, so this caused some confusion for a few minutes. The results were not matching what I expected. Only when I looked more closely did I realize my mistake. Hopefully this will help someone else.

I modified the code in the examples to provide address of the pointer being passed rather than the address of the parameter. This code was written in an Arduino Sketch.

void pointerFuncA_Original(Print* p, const char* str, int *iptr) {
  p->printf("Value of %s: %d\n", str, *iptr);
  p->printf("Address of %s: %p\n", str, iptr);
  p->printf("Address of pointer to %s: %p\n", str, &iptr);
}

void pointerFuncA(Print* p, const char* str, int **iptr) {
  p->printf("Value of %s: %d\n", str, *(*iptr));
  p->printf("Address of %s: %p\n", str, *iptr);
  p->printf("Address of pointer to %s: %p\n", str, &(*iptr));
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);

  // Wait for the serial port to connect
  while (!Serial) ;

  int i = 1234;
  int *fooi = &i;
  int j = 5678;
  int *fooj = &j;
  Serial.println("--- Original Function outputs the same address for the pointer passed to the function. ---");
  pointerFuncA_Original(&Serial, "i", fooi);
  Serial.println();
  pointerFuncA_Original(&Serial, "j", fooj);
  Serial.println();

  Serial.println("--- Modified Function Calls showing correct address of the pointer passed to the function. ---");
  pointerFuncA(&Serial, "i", &fooi);
  Serial.println();
  pointerFuncA(&Serial, "j", &fooj);
  Serial.println();

  Serial.println("--- Verify ---");
  Serial.printf("Value of i: %d\n", i);
  Serial.printf("Address of i: %p\n", &i);
  Serial.printf("Address of fooi: %p\n", &fooi);
  Serial.println();
  Serial.printf("Value of j: %d\n", j);
  Serial.printf("Address of j: %p\n", &j);
  Serial.printf("Address of fooj: %p\n", &fooj);
}

void loop() {
  // put your main code here, to run repeatedly:

}

OUTPUT:

--- Original Function Calls same address the pointer sent to the function. ---
Value of i: 1234
Address of i: 0x20041fb8
Address of pointer to i: 0x20041fa4

Value of j: 5678
Address of j: 0x20041fc0
Address of pointer to j: 0x20041fa4

--- Modified Function Calls showing correct address of the pointer sent to the function. ---
Value of i: 1234
Address of i: 0x20041fb8
Address of pointer to i: 0x20041fbc

Value of j: 5678
Address of j: 0x20041fc0
Address of pointer to j: 0x20041fc4

--- Verify ---
Value of i: 1234
Address of i: 0x20041fb8
Address of fooi: 0x20041fbc

Value of j: 5678
Address of j: 0x20041fc0
Address of fooj: 0x20041fc4

The original version displays the address of the pointers to i & j as 0x20041fa4. The modified version and the verification check show the correct addresses of the pointers to i & j that were passed to the function.

Categories
3D Printing Project

Heat Insert Press for 3D Prints

As I was building the case for the Tire Pressure Monitoring System (TPMS), I wanted to use brass inserts as threads for the screws holding the printed circuit board (PCB) in place. I had on my To Do list to build the Heat Set Insert Rig on Adafruit and designed by the Ruiz Brothers.

The guide provided by Adafruit is well written and easy to follow. I 3D printed the parts and purchased some items that I did not have. Once I was finished building it, I put it to use adding inserts into my case. The press made it much easer to position and add the brass inserts. The one problem I encountered was I did not leave enough room for the soldering iron to clear the plastic case. Fortunately I was able to work around this issue by inserting a screw into the brass insert and heat the screw instead of directly heating up the brass insert. This did take a bit more time to melt the plastic, but it worked.

I also tried a few things with 3D model for the print to help seat the brass insert a bit easier and keep melted plastic from blocking the end of the threaded insert. I made the hole a bit bigger than the diameter of the screw and made shallow ~1mm depth hole the diameter of the insert. These changes made things go together much easier and position the insert right where it needed to be. In the past, I made the diameter of the hole the same diameter or a bit smaller so the screw could bite into the plastic so the plastic and the insert hold the screw. I did away with that idea this time and made certain that I could align the insert properly and the screw could go all the way through the insert without being blocked.

How do you model your holes for brass inserts?

BTW: Here is a good YouTube video on brass inserts that I liked. I would like to try some of the better brass inserts but I have too many of the cheap ones on hand. I plan to use them up, then try some of the other ones that are specific to 3D printing.

Categories
Arduino Microcontroller Project Raspberry Pi Pico

DIY TPMS Project: Arduino-Based Tire Pressure Display

A few months ago, the TPMS light illuminated on the dash of my Toyota Prius. I looked at the tires and they looked fine, so I went to each tire measuring the pressure and they all appeared to be fine. I assumed that the battery in one of the tire sensors had died, but how can I tell for certain?

I found a project on hackster.io titled, “Arduino TPMS Tyre Pressure Display” that led me down a rabbit hole learning about TPMS. I found that there are two different types, indirect and direct. The type in my Prius are direct type, which measure the actually pressure in the tires. I also found that in addition to the tire pressure, they also report the temperature and battery health of the module.

All this information is available, but Toyota does not show this information anywhere in the 2007 Prius. If they did, it would improve safety and better experience in the following situations.

  • Stop running around the car to determine which tire is causing the dash light to illuminate.
  • When on a road trip, know if you need to pull over immediately or if it can wait until you arrive at your destination.
  • Identify slow leaks so they can be taken care of right away.
  • Improve gas milage by allowing the driver to top off the air pressure if it is a bit low, but not low enough to trigger the TPMS dash light to illuminate.

This project aims to help address the issues above and make up for Toyota not including this information on the information panel. The code and information are available in my GitHub repo at https://github.com/richteel/tpms/tree/main.

Categories
Miscellaneous Website

Site Rethink

I have been reflecting on the site and would like to be more consistent on the timing of publishing blog posts. I had the idea that blog posts should have project details but felt overwhelmed and falling short with having as many details as I felt was necessary. I am thinking that creating specific project pages will be better for the purpose of

I plan to post some things that I’m working on, product reviews, or other things that I have found interesting. The posts will have a focus on electronics, 3D printing, laser cutting, Maker Community, or STEM.

Categories
Project

Astro Camera

There will be a solar eclipse across much of Mexico, the U.S.A., and Canada on 8 April 2024. I thought about putting together a Raspberry Pi 5, Raspberry Pi HQ Camera, a telephoto lens, and building a motorized mount to move the camera to keep the sun in frame. I came across an interesting build that gets me quite a bit of what I would need for the motorized mount. I’ve been working to put it all together, but there may be too much to piece together before the eclipse. I may scale it back a bit to just move my digital SLR to track the sun. We’ll see how it goes. I’m not in the path of totality, but we will be able to see a partial eclipse. I’m hopeful that we will have a sunny day, even though we have more cloudy days this time of year.

I found the mount on YouTube by Isaac879. His project is  3D Printed DSLR Camera Pan Tilt Mount (Arduino/Stepper Driven) 2020.

I plan to add some details on the project build and lessons learned. I do not plan to work on it after the eclipse as I would like to return to the speech timer project and wrap it up.

Thanks for reading.