[{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch1\u003e\u003cstrong\u003eThe Idea\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eI've lately been dabbling with AI coding assistance and have been impressed with what it can do. So, I thought I'd do a whole project from scratch using several boards I have been meaning to do something with. I thought I'd also take you all on the journey and maybe you will find this useful. I will use this Playground article to document the process. I will go through the components and assembly, list the prompts I used with the AI tool to build the code, and share what value this new tool gives me.\u0026nbsp;\u003c/p\u003e\n\u003ch1\u003e\u003cstrong\u003eThe Build\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eThe project is a GPS tracker. In a nutshell a GPS module, an OLED display and an AdaLogger board. Here are the components I used:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/5980","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/3133","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/3045","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/3417","metadata":{}},{"element_type":"product","content":"https://www.adafruit.com/product/1781","metadata":{}},{"element_type":"text","content":"\n        \u003ch2\u003eDesign Choices\u003c/h2\u003e\n\u003cp\u003eI chose these components for simplicity. Choosing an AdaLogger for the microprocessor gives me an SD card to log the output and gives me one Neopixel, a separate LED I can use as an indicator and an extra input button with board.BUTTON. The OLED display, although small (128x32), can convey a lot of information if done well, plus it gives me three input buttons for controls. The GPS board just works well with little effort.\u0026nbsp;\u0026nbsp;\u003c/p\u003e\n\u003ch2\u003eAssembly\u003c/h2\u003e\n\u003cp\u003eSince the Feather ecosystem is perfectly modular, assembly was simple:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eSolder headers on to the microprocessor, GPS FeatherWing and the OLED FeatherWing\u003c/li\u003e\n\u003cli\u003eSolder the sockets on to the Feather Tripler\u003c/li\u003e\n\u003cli\u003ePrepare the AdaLogger by inserting a formatted SD card and attach the LiPo battery to the connector.\u0026nbsp;\u003c/li\u003e\n\u003cli\u003eInsert a coin cell into the GPS module\u003c/li\u003e\n\u003cli\u003ePlug the three boards into the Tripler - I used a couple of rubber bands and a small piece of foam on the bottom to hold it all together\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cspan\u003eThat's all there is to it! \u003c/span\u003eWith that - we are (almost) ready to code.\u003c/p\u003e\n      ","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/001/916/original/GPS_Tracker.png?1751906422","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch1\u003e\u003cstrong\u003eTo the Code\u003c/strong\u003e\u003c/h1\u003e\n\u003ch2\u003eFirst, the IDE\u003c/h2\u003e\n\u003cp\u003eBefore we get to codling, let me cover the IDE I use. For much of my history with CircuitPython I simply used Mu - great little tool. But I noticed that a lot of folks doing more complicated programming (which I aspire to) were using much more complex IDEs that color the code, suggested code completion, etc. Since I am using a Windows machine, I decided that Visual Studio Code (VSCode) was a logical choice for me. VSCode is essentially free, there are lots of extensions available, and most importantly, there are specific extensions for CircuitPython. I also signed up for a free account on GitHub \u003ca href=\"https://github.com/\"\u003ehttps://github.com/\u003c/a\u003e as this will be needed to use AI tools in VSCode and frankly, it is a great place to find lots of helpful stuff.\u003c/p\u003e\n\u003cp\u003eThere is a Learn Guide which was a great jumping-off point for me:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"guide","content":"https://learn.adafruit.com/using-the-circuitpython-extension-for-visual-studio-code/install-the-circuitpython-extension-for-vs-code","metadata":{"guide_id":"4150","page_id":"29612"}},{"element_type":"alert","content":"\u003cp\u003eI recently started working on a PC with a Snapdragon ARM processor. There is a problem with the CircuitPython extension that appears to be related to how it handles (or doesn't) serial port connections. On my Intel based PC the extensions work exceptionally well, including the board selection and the serial monitor. On my ARM PC I can write and save code with VSCode to the board, but I must keep the Mu serial monitor open on a second screen to see the REPL. This is a complication I suggest you avoid if you can.\u003c/p\u003e","metadata":{"class":"element alert-element build-alert alert-warning","markdown":"I recently started working on a PC with a Snapdragon ARM processor. There is a problem with the CircuitPython extension that appears to be related to how it handles (or doesn't) serial port connections. On my Intel based PC the extensions work exceptionally well, including the board selection and the serial monitor. On my ARM PC I can write and save code with VSCode to the board, but I must keep the Mu serial monitor open on a second screen to see the REPL. This is a complication I suggest you avoid if you can.","alert_type":"warning"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eAI Add Ons\u003c/h2\u003e\n\u003cp\u003eVSCode has integration with Microsoft's AI CoPilot. I will let CoPilot explain how to do this:\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003c/p\u003e\n\u003cp\u003eTo use GitHub Copilot in Visual Studio Code, you’ll need to install the extension and sign in with your GitHub account. Here’s a quick guide to get you started:\u003c/p\u003e\n\u003cp\u003e🛠️ Setup Steps\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cstrong\u003eInstall Visual Studio Code\u003c/strong\u003e\u003cbr\u003eIf you haven’t already, download it from \u003ca href=\"https://code.visualstudio.com/\"\u003eVisual Studio Code's official site\u003c/a\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eInstall the GitHub Copilot Extension\u003c/strong\u003e\u003c/li\u003e\n\u003cul\u003e\n\u003cli\u003eOpen VS Code.\u003c/li\u003e\n\u003cli\u003eGo to the Extensions view (\u003ccode\u003eCtrl+Shift+X\u003c/code\u003e or click the square icon on the sidebar).\u003c/li\u003e\n\u003cli\u003eSearch for \u003cstrong\u003eGitHub Copilot\u003c/strong\u003e and click \u003cstrong\u003eInstall\u003c/strong\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cli\u003e\u003cstrong\u003eSign In to GitHub\u003c/strong\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003eAfter installation, you’ll be prompted to sign in with your GitHub account.\u003c/li\u003e\n\u003cli\u003eIf you have a Copilot subscription, it’ll activate automatically. If not, you can start with the free plan which includes limited completions and chat interactions.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e✨ Using Copilot\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cstrong\u003eInline Suggestions\u003c/strong\u003e: Start typing code, and Copilot will suggest completions in gray text. Press \u003ccode\u003eTab\u003c/code\u003e to accept.\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eMultiple Suggestions\u003c/strong\u003e: Use \u003ccode\u003eAlt + ]\u003c/code\u003e and \u003ccode\u003eAlt + [\u003c/code\u003e to cycle through different suggestions.\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eCopilot Chat\u003c/strong\u003e: Press \u003ccode\u003eCtrl+I\u003c/code\u003e (or \u003ccode\u003eCmd+I\u003c/code\u003e on Mac) to open the inline chat and ask questions about your code or generate new code snippets.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eCoPilot's response to: \u003cstrong\u003ehow to use copilot in vscode\u003c/strong\u003e\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eTo the project\u003c/h2\u003e\n\u003cp\u003eFirst, we needed to install CircuitPython. Hop on over to:\u0026nbsp;\u003ca title=\"Downloads on circuitpython.org\" href=\"https://circuitpython.org/board/adafruit_feather_rp2040_adalogger/\" target=\"_blank\"\u003eAdafruit Feather RP2040 Adalogger\u003c/a\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"alert","content":"\u003cp\u003eGenerally I choose the latest Alpha release for a new CircuitPython install, however for most folks it may be best to choose the latest stable release. There is nothing in this project that relies on anything in the Alpha version that isn't in the stable version.\u003c/p\u003e","metadata":{"class":"element alert-element build-alert alert-primary","markdown":"Generally I choose the latest Alpha release for a new CircuitPython install, however for most folks it may be best to choose the latest stable release. There is nothing in this project that relies on anything in the Alpha version that isn't in the stable version.","alert_type":"primary"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eAt the same time, grab the latest library bundle for the version of CircuitPython you are installing: \u003ca title=\"Library Bundles on CircuitPython.org\" href=\"https://circuitpython.org/libraries\" target=\"_blank\"\u003ehttps://circuitpython.org/libraries\u003c/a\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eNow we need some basic sample code to lay the foundation of the project. I went to each of the Learn Guides for each of the three products and grabbed sample CircuitPython code.\u003c/p\u003e\n\u003cp\u003eFirst the AdaLogger for the basic SD card code:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"guide","content":"https://learn.adafruit.com/adafruit-feather-rp2040-adalogger/sd-card","metadata":{"guide_id":"4027","page_id":"28793"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eNext the OLED board to get basic display set-up and button code:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"guide","content":"https://learn.adafruit.com/adafruit-oled-featherwing/circuitpython-usage","metadata":{"guide_id":"1287","page_id":"17324"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eFinally, GPS FeatherWing base code:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"guide","content":"https://learn.adafruit.com/adafruit-ultimate-gps-featherwing/circuitpython-library","metadata":{"guide_id":"1318","page_id":"10822"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eI cobbled together the various pieces of sample code into a single code.py file that did the basic stuff and added all of the referenced libraries to the lib folder. This maybe took 15 minutes. I had a working project where the GPS data displayed in the REPL, I had \"Hello World\" on my OLED, I had the Neopixel showing red when the board first started and turned green once the GPS had a fix. I also had a couple of pieces of the GPS data writing to a text file on the SD card.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eNow for the magic!\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch1\u003e\u003cstrong\u003eWhy AI?\u003c/strong\u003e\u003c/h1\u003e\n\u003ch2\u003eFirst, a little detour...\u003c/h2\u003e\n\u003cp\u003eI'm a hobbyist - I have no formal training in computer science nor electronics, but I've always loved both. In high school for shop class I close electronics (we built circuits with vacuum tubes). I took Algebra 2 in an experimental class that included BASIC programming using a teletype for a terminal (no screen). Programs were saved to punched tape - cutting edge stuff in 1973. In college I worked at Radio Shack so I could play with the electronics, and then later fell in love with the TRS-80 - my first real computer. I was totally bitten by the bug.\u003c/p\u003e\n\u003cp\u003eAbout 15 years ago I heard about Arduino and Adafruit. Bought my first Arduino, installed the IDE and started playing around. First project I built was an intervalometer (which is a fancy way of saying camera shutter timer to take time lapses).\u0026nbsp; I remember struggling with writing fairly simple code, but the sense of accomplishment once I got it working was amazing.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eA couple of years later I was in the UK and visited the National Museum of Computing at Bletchley Park (I highly recommend!). They had just received a Raspberry Pi Model B and had set it up. I was amazed to see a $35 single board \"real\" computer. Took me a couple of years but got my first \"Pi\" when the 3 Model B+ came out. From there it was Raspberry Pi Pico, then Pico W, Trinkets, Feathers, etc. I found CircuitPython much easier to comprehend than Arduino code so I drifted to that.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eThis was a long way to say that I'm self-taught, use a lot of trial and error (and error and error and error), and even though I think I have put together some fairly complex code, it isn't pretty nor very efficient.\u003c/p\u003e\n\u003ch2\u003eStarting Simple\u003c/h2\u003e\n\u003cp\u003eI've seen folks talk about using AI for coding on the streams, usually as a good tool to help with repetitive tasks. I started posting simple requests in a web browser to ChatGPT and a couple of other AI models for help drafting a Python function (i.e. create a python function which converts a UTC time stamp to a differently formatted local time string). What would take me 15 minutes of fumbling around it gave me in the blink of an eye. But it was also usually Python code, not CircuitPython, and I'd have these inscrutable errors that would confound me (commands not in CircuitPython). It was helpful but also frustrating.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eI then moved on to installing the CoPilot extension in VSCode and fell in love with autocomplete - I'd start typing a line and it would figure out what I wanted to do and write it cleaner than I would on my first try, and it was also real CircuitPython. I was hooked.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eThen it stopped working - suddenly autocomplete wasn't helping me anymore. Did I break it? Turns out, there was a limit to how much it would do for free and I exceeded the limit. CoPilot told me the clock would restart at the first of the month (two weeks away). I went into withdrawal...\u003c/p\u003e\n\u003cp\u003eThen I got a message that for a small fee I could get a Pro subscription - and this would also give me access to several different AI models. I was in too deep - I had to do it!\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch1\u003e\u003cstrong\u003eOn to coding the project with AI\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eAfter signing on I set the agent to Claude Sonnet 4 and found it much more powerful than simple autocomplete. I had another project with a fairly complex display set up and multiple sets of data to display and asked it to optimize the code - it did a fantastic job.\u003c/p\u003e\n\u003cp\u003eStarting with the basic code I pulled from the learn guides, I started to give CoPilot Chat in VSCode some prompts to see what it could do. It is important that you be specific with your prompts making your wishes clear. More importantly, you have to know what to expect so that you can figure out if it actually did what you wanted it to do and that it did it correctly.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eMy prompts\u0026nbsp;\u003cem\u003ein italics\u003c/em\u003e followed by my thinking:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"TextRun SCXW102270866 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW102270866 BCX8\"\u003ecan you add code to calculate the direction of travel by the change in \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW102270866 BCX8\"\u003elatitude\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW102270866 BCX8\"\u003e and longitude, adding this bearing to the data file and display on the OLED\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e I figured that by taking the change in the GPS coordinates there was a way to determine the bearing from the last coordinate to the current coordinate\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"TextRun SCXW230763180 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW230763180 BCX8\"\u003eadd a line at 179 that will change the \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW230763180 BCX8\"\u003eneopixel\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW230763180 BCX8\"\u003e to a random color\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e I just wanted a simple way to confirm that the code was running and thought this was a good visual way to go\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"TextRun SCXW11073439 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW11073439 BCX8\"\u003eeach time the code restarts, set the name of the csv file to the current time date using the format mmddhhmm.csv\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u0026nbsp; The base code kept adding the data to the same file. I thought it would be more useful to create new files each time it restarts named by the start time.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"TextRun SCXW217588265 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW217588265 BCX8\"\u003ethe \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW217588265 BCX8\"\u003ertc\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW217588265 BCX8\"\u003e should be set using the fix timestamp received from the \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW217588265 BCX8\"\u003egps\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW217588265 BCX8\"\u003e module before using it to create the file name\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e It did this however it was using UTC for the time stamp (which is what the GPS module uses)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"TextRun SCXW88448149 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW88448149 BCX8\"\u003esince the \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW88448149 BCX8\"\u003egps\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW88448149 BCX8\"\u003e fix timestamp is \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW88448149 BCX8\"\u003eutc\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW88448149 BCX8\"\u003e, add an adjustment to US central time\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e \u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003ethis was just to set the file name to local time to avoid confusion\u0026nbsp;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"TextRun SCXW98048531 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW98048531 BCX8\"\u003ein the data save, rather than \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW98048531 BCX8\"\u003etime.monotonic\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW98048531 BCX8\"\u003e use \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW98048531 BCX8\"\u003ertc\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW98048531 BCX8\"\u003e value formatted \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW98048531 BCX8\"\u003emmddhhmmss\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e The original code used time.monotonic() for the timestamp in the file. Since we had the real time we should use that. Note that I gave it the specific format I wanted.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003eLet's kick it up a notch:\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"TextRun SCXW243460237 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW243460237 BCX8\"\u003ewhen calculating the bearing can you also calculate the distance traveled in feet from the last bearing using the change in \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW243460237 BCX8\"\u003elat\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW243460237 BCX8\"\u003e/\u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW243460237 BCX8\"\u003elon\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW243460237 BCX8\"\u003e, and add this to the data file\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e I could have Googled how to calculate this but why not let the AI do it? It suggested the Haversine formula. (I did Google that to make sure that this indeed was the way to do this calculation - it knew what it was talking about!)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"TextRun SCXW132251133 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW132251133 BCX8\"\u003ecan you display and update the number of satellites in the upper \u003c/span\u003e\u003cspan class=\"NormalTextRun ContextualSpellingAndGrammarErrorV2Themed SCXW132251133 BCX8\"\u003eright hand\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW132251133 BCX8\"\u003e corner of the display\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e Pretty simple but very useful to understand how well the GPS module should be performing. It did have a tendency to create long labels (Sats: xx) which took up too much space on the limited display so I manually changed it to \"S:x\" - if you follow along and understand the code the AI is writing you can participate!\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"TextRun SCXW101913007 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW101913007 BCX8\"\u003eeach \u003c/span\u003e\u003cspan class=\"NormalTextRun ContextualSpellingAndGrammarErrorV2Themed SCXW101913007 BCX8\"\u003etime a\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW101913007 BCX8\"\u003e new file is created add a header line at the top of the file\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e So when I open each file in Excel the columns are labeled.\u0026nbsp;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003eLet's do the buttons:\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"TextRun SCXW52340455 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW52340455 BCX8\"\u003eplease change the button checking code to use the keypad library so checking for buttons \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW52340455 BCX8\"\u003eisn't\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW52340455 BCX8\"\u003e stopping things for .1 sec and debounces things. Then add code that uses button A, B to change the interval between readings to 2.5 (current) and 15 seconds\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e The original button code (from the OLED Learn Guide) used a .1s wait to debounce - but I know that the keypad module is a better way to manage multiple buttons. Let's also use the A and B buttons to set the interval time (walking doesn't need as many data points as driving).\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"TextRun SCXW146094572 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW146094572 BCX8\"\u003eadd code that when button C is pressed, a new data file is started. Flash \"New File\" on the OLED to 2 seconds to notify of the change\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e Rather than having to restart the board to create a new file, let's assign a button to do that.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"TextRun SCXW61162295 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW61162295 BCX8\"\u003erather than the \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW61162295 BCX8\"\u003eneopixel\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW61162295 BCX8\"\u003e flashing a random color, \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW61162295 BCX8\"\u003elet's\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW61162295 BCX8\"\u003e use it to \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW61162295 BCX8\"\u003eindicate\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW61162295 BCX8\"\u003e compass directions. For due north use green, for due south use red, for anything east of north or south use yellow and anything west of north or south use magenta.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e Neoplxels should do more than flash random colors.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e\u003cspan class=\"TextRun SCXW172281231 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW172281231 BCX8\"\u003echange the function of the A and B buttons as follows: time between data points should cycle through the values in \u003c/span\u003e\u003cspan class=\"NormalTextRun ContextualSpellingAndGrammarErrorV2Themed SCXW172281231 BCX8\"\u003eintervals[\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW172281231 BCX8\"\u003e] with the A button changing to the next one and the B button to select\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e\u003cspan class=\"EOP SCXW172281231 BCX8\"\u003e We should have more options than just 2.5 of 15 seconds. I manually created a list of times (again the importance of understanding your code even if the AI is doing the heavy lifting) and told the AI how to use.\u0026nbsp;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e\u003cspan class=\"EOP SCXW172281231 BCX8\"\u003e\u003cspan class=\"TextRun SCXW40211608 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW40211608 BCX8\"\u003esince the B button is not needed to confirm then \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW40211608 BCX8\"\u003elet's\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW40211608 BCX8\"\u003e remove that functionality and save B for a different function\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e\u003cspan class=\"EOP SCXW172281231 BCX8\"\u003e\u003cspan class=\"EOP SCXW40211608 BCX8\"\u003e When it did the last change it said it didn't need to confirm but it put the B button in because I asked it to. So let's save the B button for something else.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e\u003cspan class=\"EOP SCXW172281231 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e\u003cspan class=\"EOP SCXW172281231 BCX8\"\u003e\u003cspan class=\"EOP SCXW40211608 BCX8\"\u003e\u003cspan class=\"TextRun SCXW15500392 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW15500392 BCX8\"\u003eI have added code for \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW15500392 BCX8\"\u003eboard.LED\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW15500392 BCX8\"\u003e so we can use this as an indicator to show that the code is still running. Can you add non-blocking code that will flash this led every 2 seconds (on for 2, off for 2)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e\u003cspan class=\"EOP SCXW172281231 BCX8\"\u003e\u003cspan class=\"EOP SCXW40211608 BCX8\"\u003e\u003cspan class=\"EOP SCXW15500392 BCX8\"\u003e Since the Neopixel has been put to better use, I wanted a way to indicate that everything was running OK. I added two lines of code to activate the on-board LED and let the AI code the operation. (I later changed it manually to .5 seconds). Note too I wanted to be clear to use a non-blocking approach rather than just adding a time.sleep() which is what I might have done.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW102270866 BCX8\"\u003e\u003cspan class=\"EOP SCXW230763180 BCX8\"\u003e\u003cspan class=\"EOP SCXW11073439 BCX8\"\u003e\u003cspan class=\"EOP SCXW217588265 BCX8\"\u003e\u003cspan class=\"EOP SCXW88448149 BCX8\"\u003e\u003cspan class=\"EOP SCXW98048531 BCX8\"\u003e\u003cspan class=\"EOP SCXW243460237 BCX8\"\u003e\u003cspan class=\"EOP SCXW132251133 BCX8\"\u003e\u003cspan class=\"EOP SCXW101913007 BCX8\"\u003e\u003cspan class=\"EOP SCXW52340455 BCX8\"\u003e\u003cspan class=\"EOP SCXW146094572 BCX8\"\u003e\u003cspan class=\"EOP SCXW61162295 BCX8\"\u003e\u003cspan class=\"EOP SCXW172281231 BCX8\"\u003e\u003cspan class=\"EOP SCXW40211608 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n        \u003ch3\u003eLet's step it up another notch\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"TextRun SCXW10643091 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW10643091 BCX8\"\u003eLet's\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW10643091 BCX8\"\u003e make the B button the Mode button. Mode 1 will be the current operation - interval logging mode. Mode 2 will be set-point mode. When pressing this \u003c/span\u003e\u003cspan class=\"NormalTextRun ContextualSpellingAndGrammarErrorV2Themed SCXW10643091 BCX8\"\u003ebutton\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW10643091 BCX8\"\u003e the board will change from one mode to the other, displaying the mode on the OLED for 2 seconds. In set-point mode, \u003c/span\u003e\u003cspan class=\"NormalTextRun ContextualSpellingAndGrammarErrorV2Themed SCXW10643091 BCX8\"\u003ethe a\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW10643091 BCX8\"\u003e data point will be written when the A button is pressed. The display will use the Dir to \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW10643091 BCX8\"\u003eindicate\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW10643091 BCX8\"\u003e bearing to the last set point and rather than speed this will show distance in feet from the last set point\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW10643091 BCX8\"\u003e\u0026nbsp;\u003c/span\u003e\u003c/em\u003eLet's create another mode - note that I tried to be very specific as to what I was looking for.\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"TextRun SCXW56142611 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW56142611 BCX8\"\u003ecan you create a quick help screen when \u003c/span\u003e\u003cspan class=\"NormalTextRun SpellingErrorV2Themed SCXW56142611 BCX8\"\u003eboard.BUTTON\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW56142611 BCX8\"\u003e is pressed which clears the screen of the data display and shows on each of three lines what each button does in the current mode, going back to the normal screen after 4 seconds\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW56142611 BCX8\"\u003e\u0026nbsp;\u003c/span\u003e\u003c/em\u003eThings are getting complicated - we should add a help-screen with the extra button we haven't used yet.\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"TextRun SCXW186629188 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW186629188 BCX8\"\u003eAdd a third mode - this is a modification of set point mode but instead of tracking back to the last set point always calculate from the original point, the original point being the first point after a new file is created - call this mode origin point\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u0026nbsp;\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003eThe second mode would be useful to plot out property corners, etc. Why not add a third mode that will always direct me back to the origin point, say where I parked my car.\u0026nbsp;\u003c/span\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003eGetting a little complex - \"errors\" are creeping in\u003c/span\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"TextRun SCXW159157750 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003eorigin mode \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003edoesn't\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003e seem to be working - when I change to this mode and push C to create a new \u003c/span\u003e\u003cspan class=\"NormalTextRun ContextualSpellingAndGrammarErrorV2Themed SCXW159157750 BCX8\"\u003efile\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003e I get the \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003enotification\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003e that no origin set, but pressing A \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003edoesn't\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003e appear to set the point and the \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003einitial\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW159157750 BCX8\"\u003e text never changes\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u0026nbsp;\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003eThe code is running fine but isn't necessarily exactly what I was expecting. Again - you make a change, test before moving on. Don't make a lot of changes at once in case something doesn't work. Much easier to unwind if you have to.\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"TextRun SCXW50892589 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW50892589 BCX8\"\u003efor origin mode does the current code log anything to the csv file?\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e Now I need a little help understanding what is going on in the code - so I ask. It explained the action and then asked me if I want to also log at intervals in origin mode (like mode 1 - recall that in mode 2 I am expecting it to just log each set point)\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cem\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"TextRun SCXW150271972 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW150271972 BCX8\"\u003eyes - change the origin point mode to include logging at intervals with bearing/distance calculated to the origin point\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003e Good idea - do that! The AI made several useful suggestions throughout the process, many of which I asked it to add.\u0026nbsp;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003eAs a side note I will also say that the AI didn't just make the requested changed - it provides clear and concise explanations as to what it did, and I had the chance to review and accept each of the changes.\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\n\u003ch3\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003eFew Other Tweaks\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/h3\u003e\n\u003cp\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003eAlmost done - a couple more improvements:\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cem\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003e\u003cspan class=\"TextRun SCXW150271972 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW150271972 BCX8\"\u003eyes - change the origin point mode to include logging at intervals with bearing/distance calculated to the origin point\u003c/span\u003e\u003c/span\u003e\u0026nbsp;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003c/li\u003e\n\u003cli\u003e\u003cem\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003e\u003cspan class=\"TextRun SCXW239580985 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW239580985 BCX8\"\u003ewhen in origin mode, once the distance from the origin exceeds \u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW239580985 BCX8\"\u003e2500 feet\u003c/span\u003e\u003cspan class=\"NormalTextRun SCXW239580985 BCX8\"\u003e, change the distance value displayed on the OLED to miles with one decimal however keep logging the distance value in feet for the values written to the csv file\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW239580985 BCX8\"\u003e\u0026nbsp;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003e\u003cspan class=\"EOP SCXW239580985 BCX8\"\u003eAnd last...\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cem\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003e\u003cspan class=\"EOP SCXW239580985 BCX8\"\u003e\u003cspan class=\"TextRun SCXW94859482 BCX8\"\u003e\u003cspan class=\"NormalTextRun SCXW94859482 BCX8\"\u003ereview the code for possible optimizations\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW94859482 BCX8\"\u003e\u0026nbsp;\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/em\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003e\u003cspan class=\"EOP SCXW239580985 BCX8\"\u003e\u003cspan class=\"EOP SCXW94859482 BCX8\"\u003eThe code it running very well - it is doing everything I was expecting. Although since the AI basically wrote the whole code it should already be optimized, but let's check. Interestingly it finds and fixes a few issues:\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"EOP SCXW186629188 BCX8\"\u003e\u003cspan class=\"EOP SCXW159157750 BCX8\"\u003e\u003cspan class=\"EOP SCXW50892589 BCX8\"\u003e\u003cspan class=\"EOP SCXW150271972 BCX8\"\u003e\u003cspan class=\"EOP SCXW239580985 BCX8\"\u003e\u003cspan class=\"EOP SCXW94859482 BCX8\"\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cblockquote\u003e\n\u003cp\u003e\u003cem\u003eI'll review the code for possible optimizations. Let me analyze the current implementation: After reviewing the code, I've identified several optimization opportunities. Here are the key issues and fixes:\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eSummary of Optimizations Applied\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eI've successfully optimized the code with the following improvements:\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e1. **Fixed Duplicate Code Bug**\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Removed duplicate Button C handling logic that was calling `create_new_csv_file()` twice\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e2. **Added Calculation Caching**\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Introduced cache variables to store bearing and distance calculations\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Added helper functions `calculate_origin_metrics()` and `calculate_setpoint_metrics()`\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Cache is invalidated when coordinates change or when new setpoints/files are created\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e3. **Reduced Redundant Calculations**\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- In Origin Point mode, bearing and distance are now calculated once per coordinate change instead of multiple times per loop\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Set-Point mode now uses cached calculations where possible\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Display updates and logging now use the same calculated values\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e4. **Removed Unused Import**\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Removed the unused `random` import to reduce memory footprint\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e5. **Improved Cache Management**\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Cache is properly invalidated when:\u003c/em\u003e\u003cbr\u003e\u003cem\u003e\u0026nbsp; - Coordinates change\u003c/em\u003e\u003cbr\u003e\u003cem\u003e\u0026nbsp; - New CSV files are created\u003c/em\u003e\u003cbr\u003e\u003cem\u003e\u0026nbsp; - New setpoints are recorded\u003c/em\u003e\u003cbr\u003e\u003cem\u003e\u0026nbsp; - Origin points are reset\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e6. **Memory Optimization**\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Reduced redundant function calls in the main loop\u003c/em\u003e\u003cbr\u003e\u003cem\u003e- Calculations are now shared between display updates and data logging\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003ePerformance Benefits:\u003c/em\u003e\u003cbr\u003e\u003cem\u003e1. **CPU Usage**: Reduced from ~6 calculations per GPS update to ~2-3 calculations\u003c/em\u003e\u003cbr\u003e\u003cem\u003e2. **Memory**: Eliminated redundant calculation results stored in temporary variables\u003c/em\u003e\u003cbr\u003e\u003cem\u003e3. **Responsiveness**: Faster display updates due to cached calculations\u003c/em\u003e\u003cbr\u003e\u003cem\u003e4. **Reliability**: Fixed the duplicate file creation bug\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eThe code now runs more efficiently while maintaining all functionality. The most significant improvement is in Origin Point mode where distance/bearing calculations were being performed multiple times per loop iteration.\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n        \u003ch1\u003e\u003cstrong\u003eWhat Did Learn?\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eI am certain that AI is a must-have for me and worth the small cost. Specifically:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cstrong\u003eTime\u003c/strong\u003e: With the AI's help, the entire code base took about 2 hours to write. If I didn't have the AI to do most of the work, this would have taken me days - I'm guessing 50 - 100 hours.\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eElegant, sophisticated code\u003c/strong\u003e: I've written some fairly complex code before, but it is always a mess and full of errors. The AI's code is clear and efficient.\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eEasy to Innovate\u003c/strong\u003e: One of the largest benefits is the ability to simply add new features and try them out. If I were writing this without AI help, I probably would have thought about adding modes 2 and 3 but may not have attempted just for the sheer additional work to add them in. With the AI it is a simple prompt.\u003c/li\u003e\n\u003cli\u003e\n\u003cstrong\u003eObjective optimization\u003c/strong\u003e: I thought the code was great, but by asking for a full optimization review at the end, making the code even better is super simple.\u0026nbsp;\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\"Vibe Coding\": Love it or hate it, for hobbyists I think this is way to go. Ultimately you have to know enough about the code to be able to follow and understand what is going on to make sure it is right. You have to test, and use come common sense. Don't get complacent (or lazy), and AI will be a game changer.\u0026nbsp;\u003c/p\u003e\n      \n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n        \u003ch1\u003e\u003cstrong\u003eThe code:\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eHere is the final (until I change it again) code I have running on my project:\u003c/p\u003e\n      \n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# Copyright 2025 DanaK and Claude Sonnet 4 AI\n# License: MIT License\n'''\nGPS Logger with OLED Display and CSV Logging\nThis script logs GPS data to a CSV file written to an SD card,\ndisplays it on an OLED screen, and provides real-time updates on speed, distance, and bearing.\nThis script is designed to run on a CircuitPython-compatible microcontroller\nwith GPS and OLED display support. Original hardware Adafruit Feather Adalogger RP2040\nwith GPS FeatherWing and OLED FeatherWing.\n'''\n\nimport board\nimport os\nimport time\nimport supervisor\nimport math\nimport rtc\nimport displayio\nimport terminalio\nimport digitalio\nimport busio\nimport adafruit_gps\nimport keypad\nimport storage\nimport adafruit_sdcard\nfrom adafruit_display_text import label\nfrom i2cdisplaybus import I2CDisplayBus\nimport adafruit_displayio_ssd1306\nimport neopixel as neopixel\n\ndef meters_to_feet(meters):\n    \"\"\"Convert meters to feet.\"\"\"\n    return meters * 3.28084 # 1 meter = 3.28084 feet\n\ndef knots_to_mph(knots):\n    \"\"\"Convert knots to miles per hour.\"\"\"\n    return knots * 1.15078  # 1 knot = 1.15078 mph\n\ndef knots_to_fps(knots):\n    \"\"\"Convert knots to feet per second.\"\"\"\n    return knots * 1.68781  # 1 knot = 1.68781 feet/second\n\ndef calculate_bearing(lat1, lon1, lat2, lon2):\n    \"\"\"Calculate the bearing between two GPS coordinates in degrees.\"\"\"\n    if lat1 is None or lon1 is None or lat2 is None or lon2 is None:\n        return None\n\n    # Convert to radians\n    lat1_rad = math.radians(lat1)\n    lat2_rad = math.radians(lat2)\n    delta_lon_rad = math.radians(lon2 - lon1)\n\n    # Calculate bearing\n    y = math.sin(delta_lon_rad) * math.cos(lat2_rad)\n    x = math.cos(lat1_rad) * math.sin(lat2_rad) - math.sin(lat1_rad) * math.cos(lat2_rad) * math.cos(delta_lon_rad)\n\n    bearing_rad = math.atan2(y, x)\n    bearing_deg = math.degrees(bearing_rad)\n\n    # Normalize to 0-360 degrees\n    bearing_deg = (bearing_deg + 360) % 360\n\n    return bearing_deg\n\ndef bearing_to_compass(bearing):\n    \"\"\"Convert bearing in degrees to compass direction.\"\"\"\n    if bearing is None:\n        return \"N/A\"\n\n    directions = [\"N\", \"NNE\", \"NE\", \"ENE\", \"E\", \"ESE\", \"SE\", \"SSE\",\n                 \"S\", \"SSW\", \"SW\", \"WSW\", \"W\", \"WNW\", \"NW\", \"NNW\"]\n\n    index = round(bearing / 22.5) % 16\n    return directions[index]\n\ndef calculate_distance_feet(lat1, lon1, lat2, lon2):\n    \"\"\"Calculate the distance between two GPS coordinates in feet using Haversine formula.\"\"\"\n    if lat1 is None or lon1 is None or lat2 is None or lon2 is None:\n        return None\n\n    # Earth's radius in feet (approximately 20,902,231 feet)\n    earth_radius_feet = 20902231.0\n\n    # Convert latitude and longitude from degrees to radians\n    lat1_rad = math.radians(lat1)\n    lat2_rad = math.radians(lat2)\n    delta_lat_rad = math.radians(lat2 - lat1)\n    delta_lon_rad = math.radians(lon2 - lon1)\n\n    # Haversine formula\n    a = (math.sin(delta_lat_rad / 2) ** 2 +\n         math.cos(lat1_rad) * math.cos(lat2_rad) *\n         math.sin(delta_lon_rad / 2) ** 2)\n    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))\n\n    # Distance in feet\n    distance_feet = earth_radius_feet * c\n\n    return distance_feet\n\ndef calculate_origin_metrics(current_lat, current_lon):\n    \"\"\"Calculate cached bearing and distance to origin point.\"\"\"\n    global cached_distance_to_origin, cached_bearing_to_origin, last_calc_lat, last_calc_lon\n    \n    # Check if we need to recalculate (coordinates changed)\n    if (last_calc_lat != current_lat or last_calc_lon != current_lon or \n        cached_distance_to_origin is None or cached_bearing_to_origin is None):\n        \n        if origin_lat is not None and origin_lon is not None:\n            cached_bearing_to_origin = calculate_bearing(current_lat, current_lon, origin_lat, origin_lon)\n            cached_distance_to_origin = calculate_distance_feet(current_lat, current_lon, origin_lat, origin_lon)\n            last_calc_lat = current_lat\n            last_calc_lon = current_lon\n        else:\n            cached_bearing_to_origin = None\n            cached_distance_to_origin = None\n    \n    return cached_bearing_to_origin, cached_distance_to_origin\n\ndef calculate_setpoint_metrics(current_lat, current_lon):\n    \"\"\"Calculate cached bearing and distance to set point.\"\"\"\n    global cached_distance_to_setpoint, cached_bearing_to_setpoint\n    \n    if last_setpoint_lat is not None and last_setpoint_lon is not None:\n        cached_bearing_to_setpoint = calculate_bearing(current_lat, current_lon, last_setpoint_lat, last_setpoint_lon)\n        cached_distance_to_setpoint = calculate_distance_feet(current_lat, current_lon, last_setpoint_lat, last_setpoint_lon)\n    else:\n        cached_bearing_to_setpoint = None\n        cached_distance_to_setpoint = None\n    \n    return cached_bearing_to_setpoint, cached_distance_to_setpoint\n\ndef create_new_csv_file(mode=None):\n    \"\"\"Create a new CSV file with current timestamp and mode name, write header.\"\"\"\n    global csv_filename, header_written, origin_lat, origin_lon\n    global cached_distance_to_origin, cached_bearing_to_origin, last_calc_lat, last_calc_lon\n\n    # Get current time from RTC\n    r = rtc.RTC()\n    current_time = r.datetime\n\n    # Determine mode suffix for filename\n    mode_suffix = \"\"\n    if mode == 1:\n        mode_suffix = \"_Interval_Log\"\n    elif mode == 2:\n        mode_suffix = \"_Set_Point\"\n    elif mode == 3:\n        mode_suffix = \"_Origin_Point\"\n\n    # Generate new filename based on current RTC time with mode suffix\n    csv_filename = f\"/sd/{current_time.tm_mon:02d}{current_time.tm_mday:02d}{current_time.tm_hour:02d}{current_time.tm_min:02d}{current_time.tm_sec:02d}{mode_suffix}.csv\"\n\n    try:\n        # Write CSV header to new file\n        with open(csv_filename, \"w\") as file:\n            file.write(\"Timestamp,Latitude,Longitude,Speed_ft_s,Bearing_deg,Distance_ft\\n\")\n        header_written = True\n        # Reset origin point and clear cache when new file is created\n        origin_lat = None\n        origin_lon = None\n        cached_distance_to_origin = None\n        cached_bearing_to_origin = None\n        last_calc_lat = None\n        last_calc_lon = None\n        print(f\"New GPS data file created: {csv_filename}\")\n        print(\"Origin point reset for new file\")\n        return True\n    except Exception as e:\n        print(f\"Error creating new CSV file: {e}\")\n        return False\n\ncolors = {\n    \"red\": 0xFF0000,\n    \"green\": 0x00FF00,\n    \"blue\": 0x0000FF,\n    \"yellow\": 0xFFFF00,\n    \"cyan\": 0x00FFFF,\n    \"magenta\": 0xFF00FF,\n    \"white\": 0xFFFFFF,\n    \"black\": 0x333333\n}\n\npixel = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.2, auto_write=True)\npixel.fill(colors[\"red\"])\n\n\nprint(len(colors))\ncs = digitalio.DigitalInOut(board.SD_CS)\nsd_spi = busio.SPI(board.SD_CLK, board.SD_MOSI, board.SD_MISO)\nsdcard = adafruit_sdcard.SDCard(sd_spi, cs)\nvfs = storage.VfsFat(sdcard)\nstorage.mount(vfs, \"/sd\")\n\n# CSV filename will be generated after GPS fix is obtained\ncsv_filename = \"/sd/gps_data_temp.csv\"  # Temporary filename until GPS time is available\n\ndisplayio.release_displays()\n\ni2c = board.I2C()  # uses board.SCL and board.SDA\n# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller\ndisplay_bus = I2CDisplayBus(i2c, device_address=0x3C)\ndisplay = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=32)\n\n# Make the display context\nsplash = displayio.Group()\ndisplay.root_group = splash\n\nintervals = [2.5, 5, 10, 15, 30, 60]\ncurrent_interval_index = 0  # Start with the first interval (2.5s)\n\n# Variables to track previous GPS coordinates for bearing calculation\nprev_lat = None\nprev_lon = None\ncurrent_bearing = None\nfilename_set = False  # Flag to track if we've set the filename using GPS time\nheader_written = False  # Flag to track if CSV header has been written\nreading_interval = intervals[current_interval_index]  # Initialize with first interval\nnew_file_notification = False  # Flag to show \"New File\" notification\nnotification_start_time = 0  # Time when notification started\n\n# LED flashing variables\nled_state = False  # Current LED state (False = off, True = on)\nlast_led_toggle = time.monotonic()  # Last time LED was toggled\nled_interval = 0.5  # LED toggle interval in seconds\n\n# Mode variables\ncurrent_mode = 1  # 1 = Interval Logging, 2 = Set-Point, 3 = Origin Point\nmode_notification = False  # Flag to show mode change notification\nmode_notification_start_time = 0  # Time when mode notification started\n\n# Set-point mode variables\nlast_setpoint_lat = None\nlast_setpoint_lon = None\n\n# Origin point mode variables\norigin_lat = None\norigin_lon = None\n\n# Cache variables for optimization\ncached_distance_to_origin = None\ncached_bearing_to_origin = None\ncached_distance_to_setpoint = None\ncached_bearing_to_setpoint = None\nlast_calc_lat = None\nlast_calc_lon = None\n\n# Help screen variables\nhelp_screen_active = False\nhelp_screen_start_time = 0\n\n# Initialize keypad for non-blocking button handling\nbutton_pins = (board.D9, board.D6, board.D5, board.BUTTON)\nkeys = keypad.Keys(button_pins, value_when_pressed=False, pull=True)\n\nled = digitalio.DigitalInOut(board.LED)\nled.direction = digitalio.Direction.OUTPUT\n\nuart = busio.UART(board.TX, board.RX, baudrate=9600, timeout=10)\ngps = adafruit_gps.GPS(uart, debug=False)  # Use UART/pyserial\n\n# Turn on the basic GGA and RMC info (what you typically want)\ngps.send_command(b\"PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\")\n# Turn on the basic GGA and RMC info + VTG for speed in km/h\n# gps.send_command(b\"PMTK314,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\")\n# Turn on just minimum info (RMC only, location):\n# gps.send_command(b'PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')\n# Turn off everything:\n# gps.send_command(b'PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')\n# Turn on everything (not all of it is parsed!)\n# gps.send_command(b'PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0')\n\n# Set update rate to once a second (1hz) which is what you typically want.\ngps.send_command(b\"PMTK220,1000\")\n# Or decrease to once every two seconds by doubling the millisecond value.\n# Be sure to also increase your UART timeout above!\n# gps.send_command(b'PMTK220,2000')\n# You can also speed up the rate, but don't go too fast or else you can lose\n# data during parsing.  This would be twice a second (2hz, 500ms delay):\n# gps.send_command(b'PMTK220,500')\n\n# Draw labels\nspeed_text = \"Spd: 0.0\"\nspeed_area = label.Label(terminalio.FONT, text=speed_text, color=0xFFFFFF, x=2, y=6)\nsplash.append(speed_area)\n\nbearing_text = \"Dir: N/A\"\nbearing_area = label.Label(terminalio.FONT, text=bearing_text, color=0xFFFFFF, x=2, y=17)\nsplash.append(bearing_area)\n\nlat_text = \"0.000000\"\nlat_area = label.Label(terminalio.FONT, text=lat_text, color=0xFFFFFF, x=2, y=28)\nsplash.append(lat_area)\n\nlon_text = \"0.000000\"\nlon_area = label.Label(terminalio.FONT, text=lon_text, color=0xFFFFFF, x=62, y=28)\nsplash.append(lon_area)\n\n# Add satellite count display in upper right corner\nsat_text = \"Sat: 0\"\nsat_area = label.Label(terminalio.FONT, text=sat_text, color=0xFFFFFF, x=90, y=6)\nsplash.append(sat_area)\n\n# Add reading interval display in upper right corner (below satellites)\ninterval_text = f\"Int:{reading_interval}s\"\ninterval_area = label.Label(terminalio.FONT, text=interval_text, color=0xFFFFFF, x=80, y=17)\nsplash.append(interval_area)\n\nlast_print = time.monotonic()\nwhile True:\n    # Check for button presses using keypad (non-blocking)\n    event = keys.events.get()\n    if event:\n        if event.pressed:\n            if event.key_number == 0:  # Button A\n                if current_mode == 1:  # Interval Logging Mode - cycle interval\n                    current_interval_index = (current_interval_index + 1) % len(intervals)\n                    reading_interval = intervals[current_interval_index]\n                    interval_area.text = f\"Int:{reading_interval}s\"\n                    print(f\"Button A pressed - Reading interval changed to {reading_interval} seconds (index {current_interval_index})\")\n                elif current_mode == 2:  # Set-Point Mode - record data point\n                    if gps.has_fix and gps.latitude is not None and gps.longitude is not None:\n                        print(\"Button A pressed - Recording set-point data\")\n                        # Use cached calculations if available, otherwise calculate\n                        bearing_to_setpoint, distance_to_setpoint = calculate_setpoint_metrics(gps.latitude, gps.longitude)\n\n                        # Save set-point data to file\n                        try:\n                            if header_written:\n                                r = rtc.RTC()\n                                current_rtc_time = r.datetime\n                                rtc_timestamp = f\"{current_rtc_time.tm_mon:02d}{current_rtc_time.tm_mday:02d}{current_rtc_time.tm_hour:02d}{current_rtc_time.tm_min:02d}{current_rtc_time.tm_sec:02d}\"\n                                speed_fps = knots_to_fps(gps.speed_knots) if gps.speed_knots is not None else 0.0\n                                bearing_value = bearing_to_setpoint if bearing_to_setpoint is not None else 0.0\n                                distance_value = distance_to_setpoint if distance_to_setpoint is not None else 0.0\n                                data_point = f\"{rtc_timestamp},{gps.latitude:.6f},{gps.longitude:.6f},{speed_fps:.1f},{bearing_value:.1f},{distance_value:.1f}\\n\"\n                                with open(csv_filename, \"a\") as file:\n                                    file.write(data_point)\n                                print(f\"Set-point data recorded: Lat {gps.latitude:.6f}, Lon {gps.longitude:.6f}\")\n                        except Exception as e:\n                            print(f\"Error saving set-point data: {e}\")\n\n                        # Update last set-point location and clear cache\n                        last_setpoint_lat = gps.latitude\n                        last_setpoint_lon = gps.longitude\n                        cached_distance_to_setpoint = None\n                        cached_bearing_to_setpoint = None\n                    else:\n                        print(\"Button A pressed - No GPS fix available for set-point\")\n                elif current_mode == 3:  # Origin Point Mode - cycle interval or record data point\n                    # If origin point is not set, pressing A sets it\n                    if origin_lat is None or origin_lon is None:\n                        if gps.has_fix and gps.latitude is not None and gps.longitude is not None:\n                            origin_lat = gps.latitude\n                            origin_lon = gps.longitude\n                            print(f\"Button A pressed - Origin point set: Lat {origin_lat:.6f}, Lon {origin_lon:.6f}\")\n                        else:\n                            print(\"Button A pressed - No GPS fix available to set origin point\")\n                    else:\n                        # Origin point is set, so cycle the logging interval\n                        current_interval_index = (current_interval_index + 1) % len(intervals)\n                        reading_interval = intervals[current_interval_index]\n                        interval_area.text = f\"Int:{reading_interval}s\"\n                        print(f\"Button A pressed - Reading interval changed to {reading_interval} seconds (index {current_interval_index})\")\n\n            elif event.key_number == 1:  # Button B - Mode switch\n                current_mode = current_mode + 1\n                if current_mode \u003e 3:\n                    current_mode = 1\n                mode_notification = True\n                mode_notification_start_time = time.monotonic()\n                if current_mode == 1:\n                    mode_text = \"Interval Log\"\n                elif current_mode == 2:\n                    mode_text = \"Set-Point\"\n                else:  # current_mode == 3\n                    mode_text = \"Origin Point\"\n                print(f\"Button B pressed - Switched to Mode {current_mode}: {mode_text}\")\n                \n            elif event.key_number == 2:  # Button C\n                print(\"Button C pressed - Creating new data file\")\n                if create_new_csv_file(current_mode):\n                    new_file_notification = True\n                    notification_start_time = time.monotonic()\n            elif event.key_number == 3:  # Board Button - Show help\n                help_screen_active = True\n                help_screen_start_time = time.monotonic()\n                print(\"Board Button pressed - Showing help screen\")\n\n    # Make sure to call gps.update() every loop iteration and at least twice\n    # as fast as data comes from the GPS unit (usually every second).\n    # This returns a bool that's true if it parsed new data (you can ignore it\n    # though if you don't care and instead look at the has_fix property).\n    gps.update()\n    \n    # Set origin point on first GPS fix after new file creation (or if not set) - works in any mode\n    if origin_lat is None and origin_lon is None and gps.has_fix and gps.latitude is not None and gps.longitude is not None:\n        origin_lat = gps.latitude\n        origin_lon = gps.longitude\n        print(f\"Origin point automatically set: Lat {origin_lat:.6f}, Lon {origin_lon:.6f}\")\n    \n    # Handle LED flashing (non-blocking)\n    current_time = time.monotonic()\n    if current_time - last_led_toggle \u003e= led_interval:\n        led_state = not led_state  # Toggle LED state\n        led.value = led_state\n        last_led_toggle = current_time\n\n    # Handle Mode notification display\n    if mode_notification:\n        if time.monotonic() - mode_notification_start_time \u003e= 2.0:\n            # Stop showing mode notification after 2 seconds\n            mode_notification = False\n        else:\n            # Show mode notification\n            if current_mode == 1:\n                speed_area.text = \"Mode 1\"\n                bearing_area.text = \"Int Log\"\n            elif current_mode == 2:\n                speed_area.text = \"Mode 2\"\n                bearing_area.text = \"Set-Point\"\n            else:  # current_mode == 3\n                speed_area.text = \"Mode 3\"\n                bearing_area.text = \"Origin Pt\"\n\n    # Handle Help screen display\n    if help_screen_active:\n        if time.monotonic() - help_screen_start_time \u003e= 5.0:\n            # Stop showing help screen after 5 seconds\n            help_screen_active = False\n        else:\n            # Show help screen based on current mode\n            if current_mode == 1:  # Interval Logging Mode\n                speed_area.text = \"A: Timed Log\"\n                bearing_area.text = \"B: Chg Mode\"\n                lat_area.text = \"C: New File\"\n                lon_area.text = \"\"\n            elif current_mode == 2:  # Set-Point Mode\n                speed_area.text = \"A: Create Set Point\"\n                bearing_area.text = \"B: Chg Mode\"\n                lat_area.text = \"C: New File\"\n                lon_area.text = \"\"\n            else:  # current_mode == 3  # Origin Point Mode\n                speed_area.text = \"A: Set/Chg Int\"\n                bearing_area.text = \"B: Chg Mode\"\n                lat_area.text = \"C: New File\"\n                lon_area.text = \"\"\n\n    # Handle \"New File\" notification display\n    if new_file_notification:\n        if time.monotonic() - notification_start_time \u003e= 2.0:\n            # Stop showing notification after 2 seconds\n            new_file_notification = False\n        else:\n            # Show \"New File\" notification (only if not showing mode or help notification)\n            if not mode_notification and not help_screen_active:\n                speed_area.text = \"New File\"\n                bearing_area.text = \"Created!\"\n\n    # Update display continuously (when no notifications are showing)\n    if not new_file_notification and not mode_notification and not help_screen_active and gps.has_fix:\n        if current_mode == 1:  # Interval Logging Mode\n            speed_fps = knots_to_fps(gps.speed_knots) if gps.speed_knots is not None else 0.0\n            speed_area.text = f\"Spd: {speed_fps:.1f}\"\n            if current_bearing is not None:\n                bearing_area.text = f\"Dir: {current_bearing:.0f}° {bearing_to_compass(current_bearing)}\"\n            else:\n                bearing_area.text = \"Dir: N/A\"\n        elif current_mode == 2:  # Set-Point Mode\n            if last_setpoint_lat is not None and last_setpoint_lon is not None and gps.latitude is not None and gps.longitude is not None:\n                bearing_to_setpoint, distance_to_setpoint = calculate_setpoint_metrics(gps.latitude, gps.longitude)\n                speed_area.text = f\"Dist: {distance_to_setpoint:.1f}ft\"\n                if bearing_to_setpoint is not None:\n                    bearing_area.text = f\"To: {bearing_to_setpoint:.0f}° {bearing_to_compass(bearing_to_setpoint)}\"\n                else:\n                    bearing_area.text = \"To: N/A\"\n            else:\n                speed_area.text = \"No SetPt\"\n                bearing_area.text = \"Press A\"\n        elif current_mode == 3:  # Origin Point Mode\n            if origin_lat is not None and origin_lon is not None and gps.latitude is not None and gps.longitude is not None:\n                bearing_to_origin, distance_to_origin = calculate_origin_metrics(gps.latitude, gps.longitude)\n                \n                # Display distance in miles if over 2500 feet, otherwise in feet\n                if distance_to_origin \u003e 2500:\n                    distance_miles = distance_to_origin / 5280  # Convert feet to miles\n                    speed_area.text = f\"Dist: {distance_miles:.1f}mi\"\n                else:\n                    speed_area.text = f\"Dist: {distance_to_origin:.1f}ft\"\n                \n                if bearing_to_origin is not None:\n                    bearing_area.text = f\"To: {bearing_to_origin:.0f}° {bearing_to_compass(bearing_to_origin)}\"\n                else:\n                    bearing_area.text = \"To: N/A\"\n            else:\n                speed_area.text = \"No Origin\"\n                bearing_area.text = \"Press A\"\n\n        # Update coordinates and satellite count (same for both modes)\n        lat_area.text = f\"{gps.latitude:.6f}\" if gps.latitude is not None else \"Lat: N/A\"\n        lon_area.text = f\"{gps.longitude:.6f}\" if gps.longitude is not None else \"Lon: N/A\"\n        sat_count = gps.satellites if gps.satellites is not None else 0\n        sat_area.text = f\"S:{sat_count}\"\n\n    # Check if it's time for the next reading based on current interval (Mode 1 and Mode 3)\n    current = time.monotonic()\n    if (current_mode == 1 or current_mode == 3) and current - last_print \u003e= reading_interval:\n\n        last_print = current\n\n        # Set neopixel color based on compass bearing\n        if current_bearing is not None:\n            if 350 \u003c= current_bearing or current_bearing \u003c= 10:  # Due North (350-360, 0-10)\n                pixel.fill(colors[\"green\"])\n            elif 170 \u003c= current_bearing \u003c= 190:  # Due South (170-190)\n                pixel.fill(colors[\"red\"])\n            elif 10 \u003c current_bearing \u003c 170:  # East of North/South (10-170)\n                pixel.fill(colors[\"yellow\"])\n            elif 190 \u003c current_bearing \u003c 350:  # West of North/South (190-350)\n                pixel.fill(colors[\"magenta\"])\n        else:\n            # No bearing available, use default color\n            pixel.fill(colors[\"red\"])\n\n        if not gps.has_fix:\n            # Try again if we don't have a fix yet.\n            print(\"Waiting for fix...\")\n            continue\n        # We have a fix! (gps.has_fix is true)\n        # Print out details about the fix like location, date, etc.\n        print(\"=\" * 40)  # Print a separator line.\n        print(\n            \"Fix timestamp: {}/{}/{} {:02}:{:02}:{:02}\".format(  # noqa: UP032\n                gps.timestamp_utc.tm_mon,  # Grab parts of the time from the\n                gps.timestamp_utc.tm_mday,  # struct_time object that holds\n                gps.timestamp_utc.tm_year,  # the fix time.  Note you might\n                gps.timestamp_utc.tm_hour,  # not get all data like year, day,\n                gps.timestamp_utc.tm_min,  # month!\n                gps.timestamp_utc.tm_sec,\n            )\n        )\n        print(f\"Fix quality: {gps.fix_quality}\")\n        # Some attributes beyond latitude, longitude and timestamp are optional\n        # and might not be present.  Check if they're None before trying to use!\n        if gps.satellites is not None:\n            print(f\"# satellites: {gps.satellites}\")\n        print(time.monotonic())\n        print(f\"Latitude: {gps.latitude:.6f} degrees\")\n        print(f\"Longitude: {gps.longitude:.6f} degrees\")\n        # print(f\"Precise Latitude: {gps.latitude_degrees} degs, {gps.latitude_minutes:2.4f} mins\")\n        # print(f\"Precise Longitude: {gps.longitude_degrees} degs, {gps.longitude_minutes:2.4f} mins\")\n        # if gps.altitude_m is not None:\n        #     print(f\"Altitude: {meters_to_feet(gps.altitude_m)} feet\")\n        if gps.speed_knots is not None:\n            print(f\"Speed: {knots_to_fps(gps.speed_knots)} feet/second\")\n        # if gps.speed_kmh is not None:\n        #     print(f\"Speed: {gps.speed_kmh} km/h\")\n        # if gps.track_angle_deg is not None:\n        #     print(f\"Track angle: {gps.track_angle_deg} degrees\")\n        # if gps.horizontal_dilution is not None:\n        #     print(f\"Horizontal dilution: {gps.horizontal_dilution}\")\n        # if gps.height_geoid is not None:\n        #     print(f\"Height geoid: {gps.height_geoid} meters\")\n\n        # Update OLED display\n        speed_fps = knots_to_fps(gps.speed_knots) if gps.speed_knots is not None else 0.0\n\n        # Set RTC and generate filename from GPS time on first fix\n        if not filename_set:\n            # Convert GPS UTC time to US Central Time (UTC-6)\n            # Note: This doesn't account for daylight saving time automatically\n            utc_time = gps.timestamp_utc\n            central_offset_hours = -5  # US Central Standard Time is UTC-5\n\n            # Calculate Central Time by adjusting hours\n            central_hour = utc_time.tm_hour + central_offset_hours\n            central_day = utc_time.tm_mday\n            central_mon = utc_time.tm_mon\n            central_year = utc_time.tm_year\n\n            # Handle day rollover for negative hours\n            if central_hour \u003c 0:\n                central_hour += 24\n                central_day -= 1\n                if central_day \u003c 1:\n                    # Handle month rollover (simplified)\n                    central_mon -= 1\n                    if central_mon \u003c 1:\n                        central_mon = 12\n                        central_year -= 1\n                    # Set day to last day of previous month (simplified to 30)\n                    central_day = 30\n\n            # Handle day rollover for hours \u003e= 24\n            elif central_hour \u003e= 24:\n                central_hour -= 24\n                central_day += 1\n                # Simplified month rollover (assuming 31 days max)\n                if central_day \u003e 31:\n                    central_day = 1\n                    central_mon += 1\n                    if central_mon \u003e 12:\n                        central_mon = 1\n                        central_year += 1\n\n            # Create Central Time struct_time\n            central_time = time.struct_time((\n                central_year, central_mon, central_day,\n                central_hour, utc_time.tm_min, utc_time.tm_sec,\n                utc_time.tm_wday, utc_time.tm_yday, utc_time.tm_isdst\n            ))\n\n            # Set RTC using Central Time\n            r = rtc.RTC()\n            r.datetime = central_time\n            print(f\"RTC set from GPS timestamp (converted to Central Time: {central_mon:02d}/{central_day:02d}/{central_year} {central_hour:02d}:{utc_time.tm_min:02d}:{utc_time.tm_sec:02d})\")\n\n            # Generate CSV filename based on Central Time (mmddhhmm.csv) with mode suffix\n            mode_suffix = \"\"\n            if current_mode == 1:\n                mode_suffix = \"_IntLog\"\n            elif current_mode == 2:\n                mode_suffix = \"_SetPt\"\n            elif current_mode == 3:\n                mode_suffix = \"_Origin\"\n            \n            csv_filename = f\"/sd/{central_mon:02d}{central_day:02d}{central_hour:02d}{utc_time.tm_min:02d}{mode_suffix}.csv\"\n            print(f\"GPS data will be saved to: {csv_filename}\")\n            filename_set = True\n\n            # Write CSV header to new file\n            try:\n                with open(csv_filename, \"w\") as file:\n                    file.write(\"Timestamp,Latitude,Longitude,Speed_ft_s,Bearing_deg,Distance_ft\\n\")\n                header_written = True\n                print(\"CSV header written to file\")\n            except Exception as e:\n                print(f\"Error writing CSV header: {e}\")\n\n        # Calculate bearing and distance based on current mode\n        distance_feet = 0.0\n        bearing_value = 0.0\n        \n        if current_mode == 1:  # Interval Logging Mode - calculate from previous coordinates\n            if prev_lat is not None and prev_lon is not None and gps.latitude is not None and gps.longitude is not None:\n                current_bearing = calculate_bearing(prev_lat, prev_lon, gps.latitude, gps.longitude)\n                distance_feet = calculate_distance_feet(prev_lat, prev_lon, gps.latitude, gps.longitude)\n                bearing_value = current_bearing if current_bearing is not None else 0.0\n        elif current_mode == 3:  # Origin Point Mode - calculate from origin\n            if origin_lat is not None and origin_lon is not None and gps.latitude is not None and gps.longitude is not None:\n                bearing_to_origin, distance_to_origin = calculate_origin_metrics(gps.latitude, gps.longitude)\n                bearing_value = bearing_to_origin if bearing_to_origin is not None else 0.0\n                distance_feet = distance_to_origin if distance_to_origin is not None else 0.0\n\n        # Update display (only if not showing \"New File\" notification)\n        if not new_file_notification and not mode_notification:\n            # Display updates are handled in the continuous section above\n            pass\n\n        # Always update coordinates and satellite count during GPS readings\n        lat_area.text = f\"{gps.latitude:.6f}\" if gps.latitude is not None else \"Lat: N/A\"\n        lon_area.text = f\"{gps.longitude:.6f}\" if gps.longitude is not None else \"Lon: N/A\"\n        sat_count = gps.satellites if gps.satellites is not None else 0\n        sat_area.text = f\"S:{sat_count}\"\n\n        try:\n            # Save data to file with bearing and distance using RTC timestamp\n            # Only write data if header has been written\n            if header_written:\n                r = rtc.RTC()\n                current_rtc_time = r.datetime\n                rtc_timestamp = f\"{current_rtc_time.tm_mon:02d}{current_rtc_time.tm_mday:02d}{current_rtc_time.tm_hour:02d}{current_rtc_time.tm_min:02d}{current_rtc_time.tm_sec:02d}\"\n                distance_value = distance_feet if distance_feet is not None else 0.0\n                data_point = f\"{rtc_timestamp},{gps.latitude:.6f},{gps.longitude:.6f},{speed_fps:.1f},{bearing_value:.1f},{distance_value:.1f}\\n\"\n                with open(csv_filename, \"a\") as file:\n                    file.write(data_point)\n        except Exception as e:\n            print(f\"Error saving data: {e}\")\n\n        # Display bearing and distance in console based on mode\n        if current_mode == 1:  # Interval Logging Mode\n            if prev_lat is not None and prev_lon is not None:\n                if current_bearing is not None:\n                    print(f\"Bearing: {current_bearing:.2f} degrees\")\n                    compass_direction = bearing_to_compass(current_bearing)\n                    print(f\"Compass Direction: {compass_direction}\")\n                if distance_feet is not None:\n                    print(f\"Distance traveled: {distance_feet:.1f} feet\")\n        elif current_mode == 3:  # Origin Point Mode\n            if origin_lat is not None and origin_lon is not None:\n                if bearing_value != 0.0:\n                    print(f\"Bearing to origin: {bearing_value:.2f} degrees\")\n                    compass_direction = bearing_to_compass(bearing_value)\n                    print(f\"Compass Direction to origin: {compass_direction}\")\n                if distance_feet is not None:\n                    print(f\"Distance to origin: {distance_feet:.1f} feet\")\n\n        # Update previous coordinates for next calculation\n        if gps.latitude is not None and gps.longitude is not None:\n            prev_lat = gps.latitude\n            prev_lon = gps.longitude","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n        \u003ch1\u003e\u003cstrong\u003eGreat big Thank-You!\u003c/strong\u003e\u003c/h1\u003e\n\u003cp\u003eto the whole Adafruit team. Not only do you have great products, but all of the resources you make available are fantastic. The commitment to open-source and investments in CircuitPython are legendary! Thanks for all you do!\u003c/p\u003e\n      \n\n\n\n","metadata":{}}]