#BB9

แชร์
ฝัง
  • เผยแพร่เมื่อ 26 พ.ค. 2022
  • Organised code is so much easier to read, debug and maintain.
    ► PCBWay $5 for 10 pieces www.pcbway.com
    "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." - Martin Fowler (en.wikiquote.org/wiki/Martin_...)
    ► Summary
    I was taught early on in my computer programming career that beautiful code indicates a tidy, organised mind too - and probably leads to code that works first time, is easily understood by others and can be maintained in the years to come.
    In this video we look at how we turn "prototyping code" into a well-structured, modular program by separating the various components of the program into separate files.
    All the code in the video is available in the GitHub for you to play around with:
    github.com/RalphBacon/BB9-Cod...
    ► Hardware used in demo project:
    LCD screen INCL I2C backpack (ships from UK): www.banggood.com/custlink/Dmm...
    Separate Backpack (for bare LCD screen): www.banggood.com/custlink/KmD...
    DS18b20 temperature sensor (TEN pieces): s.click.aliexpress.com/e/_AYM61d
    DS18b20 Waterproof version (1m cable): s.click.aliexpress.com/e/_9fiBAb
    Pictures and more info in my GitHub: github.com/RalphBacon/BB9-Cod...
    ► Next Steps
    The next video (BB10 in this series) progresses this theme and will cover namespaces - which goes some way towards getting rid of true globals. Grr!
    Stay tuned!
    ► Useful or interesting video? You can support my channel:
    Buy Me A Coffee! www.buymeacoffee.com/ralphbacon
    ► List of all my videos (Special thanks to Michael Kurt Vogel for compiling this)
    bit.ly/TH-camVideoList-RalphB...
    ► If you like this video please give it a thumbs up, share it and if you're not already subscribed please consider doing so and joining me on my Arduinite (and other μControllers) journey
    My channel, GitHub and blog are here:
    ------------------------------------------------------------------
    • / ralphbacon
    • ralphbacon.blog
    • github.com/RalphBacon
    • buymeacoffee.com/ralphbacon
    ------------------------------------------------------------------
  • วิทยาศาสตร์และเทคโนโลยี

ความคิดเห็น • 179

  • @Cptnbond
    @Cptnbond 2 ปีที่แล้ว +2

    You are the master of all YT code and hardware teachers in the way you inspire and make it simple to understand - whatever topic you talk about. Cheers.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Thank you for your kinds words, most appreciated. I wonder if you will say the same after my video on Pointers? 😲

    • @Cptnbond
      @Cptnbond 2 ปีที่แล้ว +1

      @@RalphBacon We will see. Lol.

  • @simonbaxter8001
    @simonbaxter8001 2 ปีที่แล้ว +1

    It's not only easier to read and debug ... it also makes your code 'blocks' reusable in other projects without having to write portions of code again as you've essential created your own mini library for your projects!

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      That is so true and not something I consciously thought about - but I reuse blocks of my code from other projects all the time!

  • @N6ROB
    @N6ROB ปีที่แล้ว +3

    Just wanted to say that this video (and BB#10) has really helped me improve my code. As my program gets larger, and larger (with more features), this method of organizing code has really helped me to think more clearly about how it should be structured. I'm not especially good at it, and I struggle sometimes with moving variables from one place to another, but at least I am heading the right direction. I was even inspired enough to create a git hub page - yikes! This, from a guy who is more comfortable with a solder iron than a keyboard and compiler! Thanks Ralph.

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      This is excellent news, thanks for the update. Yes, software can be scary for hardware engineers but as my (development manager) boss used to tell us, "It's just typing". That upset a few people that day!

  • @jstro-hobbytech
    @jstro-hobbytech 2 ปีที่แล้ว +3

    Very, very good video. You're raising up everyone who wants to learn. It makes code reusable too for other projects.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Glad you think so! Namespaces soon then!

    • @jstro-hobbytech
      @jstro-hobbytech 2 ปีที่แล้ว

      @@RalphBacon I use std usually haha. I love pure c++. Even though I'm not a professional like you I'm familiar with most concepts but i dont code enough. Ive been very circuit oriented lately with the theory and breadboarding different concepts. I have so many breadboards that if its something i dont really understand fully they get placed on a shelf infront of me until they do. I have a few giant ones that you can put power in the jacks but i want one that has different built voltage supplies so you can have negative voltage rails. They are absurdly expensive so ill probably end up just making my own out of the new one i have that has over 16k tie points lol. I'm loving these videos Ralph so keep them coming. Do you have patreon?

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      I've just ordered two more breadboards as I keep leaving the circuits on them for too long whilst I think about a video! And I have some large ones too but frankly they are too big for most uses. Except my ESP32 Web Radio, that needed a big one.
      I don't have a Patreon but if you want to support me occasionally check out my video description or GitHub as I have a "Buy me a coffee" link which many of my supporters have used in the past. ☕

  • @whitefields5595
    @whitefields5595 2 หลายเดือนก่อน

    Another benefit of this approach is that once something is working in a separate tab you can leave it alone. If its all the the same place you can make unintentional silly mistakes that take ages to find

    • @RalphBacon
      @RalphBacon  2 หลายเดือนก่อน

      Absolutely. Combine the separate tabs approach with namespaces and the "silly mistakes" that you mention (we've all done it) simply cannot be unintentionally made.

  • @TimSeymour
    @TimSeymour ปีที่แล้ว +1

    As a rank hobbyist, this was SOOOO helpful... my code was getting out of control as I kept adding more features to my project. I have reworked my sketch and have found that the order of declaring the #includes is important. So satisfying to clean house! THANK YOU

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      Glad it helped! 👍
      Yes, the order of the #includes is important. Remember, too, that you can include other files into each of the individual header files (as long as you remember to put the #pragma once at the top.
      So a header file of definitions might be included in just about every other header file as well as the main sketch. But that's fine and ensures the compiler can "see" all the variables at compile time - but just the once!

  • @ifell3
    @ifell3 2 ปีที่แล้ว +1

    Wow this is great, never knew you could add tabs etc!

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +2

      Glad you like it! And, yes, do try it out, it really makes a difference.

  • @willofirony
    @willofirony 2 ปีที่แล้ว +2

    When I start a project I tend to use a python script to generate the file(s). Each file has the boilerplate code (I still use the old fashioned include guards rather than the once pragma) , common includes etc. The organisation of the body of the code is "suggested" by a series of comments as is the more optional includes (less common includes can be 'included' by arguments to the script). I do this for desktop projects and embedded projects (there are a number of scripts for each environment). I find this reduces errors (Ok it also panders to my laziness too). More importantly the "suggested" structure of the code imparts a discipline that aids debugging appreciably. Without getting too arty farty , writing code is a creative process (albeit riddled with plagiarisms). Most often this is done away from the keyboard, on the way to work etc. So, when the opportunity to commit these ideas to written code presents itself, it is accompanied by some impatience. One wants to get that algorithm that will revolutionise the software industry on the disk before it disappears like those plans for the flying car did. So having a disciplined approach to getting that into the memory of a computer at least suggested can result in a working prototype. With desktop projects the scripts can be more elaborate, decorating the files with keywords (commented) that can be used later by yet another script to generate a cmakelists.txt file for the build. I am enjoying this series of tutorials immensely, Ralph. Well done you!

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      You are very organised and disciplined, Michael. Except for the bit about the flying car designs 😜, of course. 'Nuff said.
      At work we occasionally had a 'sprint' where we refactored the code - it really made a difference to the overall quality and allowed us time to do the things we'd wish we'd done first time around (if we'd had the time! - Yes, I see the irony).
      Anyway, I'm glad you're enjoying the videos!

  • @Roel_Scoot
    @Roel_Scoot ปีที่แล้ว

    I had since some time discovered the tabs for housekeeping larger programs, but did not know exactly how to implement them. Now I know, thank you!

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      Excellent! Now get organising your code, it will feel good!

  • @jackdowning8841
    @jackdowning8841 2 ปีที่แล้ว

    Probably one of the best code organisation videos I've every watched, cheers Ralph!

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Great to hear! Now go and try it out or download my example sketch, Jack!

  • @gordondyer6310
    @gordondyer6310 2 ปีที่แล้ว +2

    I'm new(ish} to Arduino and have written a 1600 line program all in one .ino file!
    Thanks for the lesson, some day I will try to untangle my spaghetti into several files.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      You can do it! Keep you busy, anyway!

  • @joshlikessurfing
    @joshlikessurfing 2 ปีที่แล้ว

    Glad you did it the "lazy way". I have always wanted to do more .h and .cpp file setup but it just felt like so much. This is a great stepping stone.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Glad it was helpful! Yes, splitting the header and code files is a right chore. And when there is no boss to say "do it!" then we don't have to!

  • @tseckwr3783
    @tseckwr3783 ปีที่แล้ว

    thanks Ralph. it is always good to be organized in code.

  • @RupertBruce
    @RupertBruce 2 ปีที่แล้ว +1

    Essentially, you are using namespaces to group static functions instead of building classes, instantiating and hopefully destroying them. I like it! Minimal syntax and no memory leaks - great organizational tool for these projects. 😎

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Hah! You said it!
      Classes are not easy for beginners (although they are using them all the time with all those library functions and objects!) But, yes, this starts making developers think about where all the code has to go. Separation of concerns and all that.

  • @PeranMe
    @PeranMe 2 ปีที่แล้ว

    Good job as always Ralph!

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Thank you! Cheers!

  • @200sxgrazor
    @200sxgrazor 2 ปีที่แล้ว

    Many thanks for this, Ralph. I've just refactored my current project to use this organisation structure and it still works!!!! :) Looking forward to the next instalment.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Fantastic! But don't sound so surprised! 😁 Namespaces this Friday before I take a break!

    • @200sxgrazor
      @200sxgrazor 2 ปีที่แล้ว

      @@RalphBacon I spoke too soon. I've tried taking it a bit further and attempted to make better use of functions but I'm now running into various scope issues :( but I should add these are self inflicted by bad coding practices. this confused me for a bit but I'm managing to sort them out :)

  • @IanSlothieRolfe
    @IanSlothieRolfe 2 ปีที่แล้ว +2

    Just to add my usual 10p ramblings, I would use # defines to define pin numbers for hardware connections and address settings etc and put them in the globals.h rather than scattering them around in the helper files - this will make porting the code to a different microcontroller board (e.g from an Arduino to an ESP32) much cleaner, you'd just have to update the globals.h file. Since these definitions do not relate to a namespace (i.e. they are constant across the whole project by definition) then putting them in a namespace may be arbitrary and make them hard to find in a complex projecct.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      It's six of one and half-a-dozen of the other in my mind.
      Sometimes I do it the way you suggest, sometimes I put the #defines in the helper file to which they pertain. In the end it makes no difference, of course, but grouping the not-quite globals together is probably the better way of doing it.
      Using constexpr instead of #define also allows the compiler to be efficient but then we can also use namespaces (next video!).

  • @ChrisBalmforth
    @ChrisBalmforth ปีที่แล้ว

    I just discovered your channel this morning and it's perfect for me as a self-taught hobbyist robotics programmer. I want to use good practices but it's been difficult for me to find suitable tutorials. Keep the good stuff coming along!

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว +1

      Welcome aboard! Now watch the other 252 videos 😲😂

  • @maxximumb
    @maxximumb 2 ปีที่แล้ว +1

    Another excellent video.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Glad you enjoyed it!

  • @markday3145
    @markday3145 2 ปีที่แล้ว

    Your starting code was already quite well organized, making the benefits of the reorganization less apparent. What really made a difference there was the addition of a few wrappers/convenience functions. Those could have just as easily stayed in the one source file. For a project that small, breaking it up into multiple files is less critical. For bigger projects, breaking things up can be a huge improvement.
    I'm glad you're going to cover namespaces. They are great for organizing code and simplifying naming.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      As I edited my video I realised I should have made the code starting point more typical of what I see! Mine was already far too tidy! Yes, namespaces are easy to understand and give useful benefits.

  • @byronwatkins2565
    @byronwatkins2565 2 ปีที่แล้ว +2

    If your compiler (pre-processor) doesn't recognize #pragma once, you can use 'include guards' instead:
    #if !defined (UNIQUE_H)
    #define UNIQUE_H
    /* Your code here. */
    #endif // UNIQUE_H
    You typically use your header file name as your unique constant. Managing this yourself, you risk complications like another piece of code using the same constant, forgetting one of the three lines, typo, interactions with other #if constructs, etc. If possible, #pragma once is much more robust and convenient; so, use include guards as Plan B.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      Indeed, that's the bit I said "I won't go into that now!".
      For beginners, include guards are clunky and beginners forget the #endif at the end! "#pragma once" is easily understood and all _modern_ compilers accept it, just the legacy ones not.

  • @Tony770jr
    @Tony770jr 2 ปีที่แล้ว

    Another great video. I haven't thought too much of using the namespace feature in Arduino code, but makes things less ambiguous. Keep the videos coming!

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Glad it was helpful! But we'll cover the namespace feature in more depth next video (in this series).

  • @mr.bianchirider8126
    @mr.bianchirider8126 2 ปีที่แล้ว +1

    One frustrating occurrence while coding is trying to find a particular line out of 500+ lines. By using tabs to compartmentalize ( hmm.. is that a word ?) the code, it will make it easier to debug. Thank you and please continue on this subject.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +2

      I'm glad you found it good food for thought. Next video in this series is about namespaces which goes a little further. You will find that equally interesting I should think.

  • @johnstephenson2891
    @johnstephenson2891 2 ปีที่แล้ว +1

    My software development company in California had about 18 programmers(?). Maybe half wrote clear understandable code! I gave them two options, 1) re-write the code so it read like a book! or write two lines of comments per line of code!!!

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      Yes, a few comments can be very useful to the next programmer who has to maintain that code (probably yourself).
      The problem with comments is that they might just describe WHAT the code does rather than WHY or HOW.
      For example (made up example):
      // Set the engine's intermix ratio
      bool success = (setInterMixCoolerRatio(14.983 * &WarpEngineFactor / AntiMatterRatio)^MaxSpeed) % 50;
      That comment tells you nothing that could not be gleaned from reading the code. What's missing is how it's doing it and why, amongst other things, it's using the magic number 14.983 (just as an example, because everyone knows that's the accepted conversion factor for all anti-matter engines). 😜

  • @stevex3976
    @stevex3976 ปีที่แล้ว

    Very interesting. This is something I have been looking for to clean up programs. Thanks!

  • @espedito2003
    @espedito2003 2 ปีที่แล้ว +1

    Excellent topic.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Thank you, glad you found it interesting.

    • @espedito2003
      @espedito2003 2 ปีที่แล้ว +1

      @@RalphBacon Sure did! Greetings from Brazil.

  • @atilliator
    @atilliator 2 ปีที่แล้ว +1

    perfect, just started doing this myself

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Excellent! And you can use namespaces too to improve it, next video in the series!

  • @arnotek
    @arnotek ปีที่แล้ว

    Thank you! I have been writing software for over half a century (egads - where did the time go?) and I am still learning. This is spot on - even for a hobbyist. Don't believe me? - then go back and look at something that you wrote 6 months ago. I have always subscribed to writing code so the person coming in behind me would understand what the code was doing, including lots of comments. The mantra of "the code is self-documenting" is very naive. I also like your comment of putting code in the .h file. (or .hpp) It is clean and concise and reduces unnecessary files that would add to the complexity of trying to keep everything straight. Yeah, the purist heads will explode - but I think it is the way to go. Thank you again for the effort you put into all of your videos. And yes, you can teach an old dog new tricks. :o)

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      Thanks, Arno. I've only been writing code (for a living) for about 42 years so well behind you. I've seen quite a sea-change of concepts in that time, all very exciting (at the time). These days, I tend to think "Micro Python on a Pi? Great." and just continue with my C++ work!

  • @awuk3468
    @awuk3468 2 ปีที่แล้ว +1

    Many "professional" environments are "fair game" to source code obfuscatory practices, many monolithic single .c/cpp file to run an entire system. :: is the scope resolution operator, which means you can access namespaces etc using it. Typically header files are for declaration and associated c/cpp files are for implementation but you can write everything in the header file, it works the same mainly convention, some libraries are just a .h file. #pragma once is a replacement for the legacy header include guard (#ifndef _XXXX_H #define _XXXX_H ... #endif), you can use both, most modern compilers support the #pragma once but some legacy code which require certain older compilers may not recognise it.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      Talking of "obfuscatory practices" I've seen a few files full of hex that is actually code to be executed. So much for "Open Source" in some cases 😬

    • @awuk3468
      @awuk3468 2 ปีที่แล้ว

      @@RalphBacon Unfortunately some think "open source" is supplying the HEX / firmware file, so I guess supplying a source file with the hex code inside is the next evolution of that. Although what a lot seem to forget is that anything that can be compiled can be decompiled, just takes a bit more time.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Indeed. In one case I found an online "decompiler". The "encrypted" source was in hex but it was standard source code, not compiled. Ascii, basically. So it was more a case of code obfuscation than just supplying the compiled code.

  • @JulioSalim
    @JulioSalim 2 ปีที่แล้ว

    You are the best!!! Thank you!!!

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Wow! High praise indeed, Julio, thank you!

  • @MUHAMMADYAWARIFRAHEEM
    @MUHAMMADYAWARIFRAHEEM 2 ปีที่แล้ว

    Very informative video Sir 👍

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Glad you liked it!

  • @BerndFelsche
    @BerndFelsche 2 ปีที่แล้ว +1

    It also makes code easier to reuse in realtime threads. Well, when you name it as code instead of header. 😜

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      Hehe! Yes, I thought you might object. 🙄
      I knew some developers would object to using header files for code, hence my brief explanation in the video that for hobbyists it's probably "good enough" and allows beginners to understand the concept of code modularisation without the added complexity of multiple headers and multiple matching cpp files. It can easily overwhelm a beginner who is trying to separate concerns and adhere to the single responsibility principle.
      But if you want to do it 100% correctly, we should indeed put executable code into cpp files and only declarations into header files. 😉

    • @g0hjq
      @g0hjq 2 ปีที่แล้ว +1

      @@RalphBacon I've seen massive Linux libraries written purely in .h files. I suppose they must have their reasons.

  • @andymouse
    @andymouse 2 ปีที่แล้ว

    Interesting stuff, I need to be tidier !...cheers.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      You do Andy, and so does your code. 😉

    • @andymouse
      @andymouse 2 ปีที่แล้ว +1

      @@RalphBacon Owch !!🤣...cheers !

  • @webslinger2011
    @webslinger2011 2 ปีที่แล้ว +1

    Simple but very useful for beginners like me. Been fooling around with 3d printer Marlin firmware wondering how it’s all setup.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Glad it was helpful! Marlin firmware? good luck with that!

    • @joshlikessurfing
      @joshlikessurfing 2 ปีที่แล้ว +2

      Best tools for Marlin are configuration file, marlin main file and control f. If you use vs code you can track down references to functions. Think sublime text does this as well.

  • @zeferby
    @zeferby 2 ปีที่แล้ว

    Excellent video Ralph!
    Edit: having paged down further into the comments, I now see multiple references to the same traditionnal technique... :-)
    About the #pragma portability question : I more often use something "manual" like this in my various code and header files :
    Header file :
    #ifndef included_filename_symbol_h
    #define included_filename_symbol_h
    ...
    contents of header file here
    ...
    #endif included_filename_symbol_h
    Code file :
    #ifndef included_filename_symbol_h
    #include "included_filename.h" (or instead of "...", or hpp instead of h, depending on file location, etc...)
    #endif included_filename_symbol_h
    ...
    use header declarations in code
    ...
    NB: some compilers require the #endif to be written alone without the symbol name; I like my #endifs to have the related symbol name when possible to avoid confusion when I have a lot of #endifs one after the other...

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Yes, I use
      #endif // __myDef_name__
      to make it clear too.

  • @coxsj
    @coxsj 2 ปีที่แล้ว

    +1 for Bacon bytes!! Love it!!

  • @pfeerick
    @pfeerick 2 ปีที่แล้ว

    Great video Ralph... And also damn you... now I got to go clean up some of my code! 😂😂 I use helper setupWifi(), setupOTA(), setupMQTT() etc but hadn't go to the extent of separate files or namespaces. btw, this isn't the first time I've seen header only implementation.... and I think I've even seen somewhere that it was more than "just being lazy"... that there was some compile efficiency reason to do it sometimes...

    • @berthold64
      @berthold64 2 ปีที่แล้ว +1

      I'd suggest put those into .cpp with header declaration. Believe me, big header file with implementation can impact your compilation time. You'd be thankful knowing how cpp and h files link together. You may also learn static linking (.a/.lib) files too, it can be useful down the road.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      The first step is always to stand up and say "Hi, I'm Ralph and I'm a ... messy coder". Then split your files as I have shown. Next video we'll talk about namespaces but it's more of an evolution than revolution to what we have here already.
      If you really want to do it 100% correct (as null says here) and split the header into .h and .cpp files then by all means do it, but IMHO it's a diminishing return on investment for hobbyists.
      Each file must have visibility of variables that it refers to or your IDE will barf, so you must include the necessary .h files as required - all with #pragma once, of course.
      Start small, as I showed, and work up from there. You know it's/we're worth it. 😜

  • @1over137
    @1over137 ปีที่แล้ว

    Tip. If you are using a ds1820 temp sensor in it's "waterproof" capsule. It's not waterproof! The capsule has a bit of sticky snot in it, the sensor is stuffed in and the heatshrink applied. If you want to use it outdoors always mount it so the silver capsule it pointed upwards to allow condensation to drain out. Other wise after about 6 months to a year it will eventually fill with water, the glue will degrade and it will stop working. Mine worked again for a few days after I dried it out, but then it became intermittent and I had to replace it.

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      Good pointer, Paul.
      I've also found that if the heat shrink is removed (it's not totally watertight) and replaced with adhesive heat shrink (normally shrinks to 3:1 ratio too) then it's fine.

  • @NigelAtkinson256
    @NigelAtkinson256 2 ปีที่แล้ว

    I've worked with "header only" libraries in the world of big computers, with all their code in headers or even just one header. While making separate cpp files might seem more "proper" I see no reason to complicate things. However if you're project is really getting bigger then that will help. One benifit of separate cpp files is that if you don't change the code, they don't need re-compiling which is great if they are really getting on the bigger side or you have a lot of them. That way only the file(s) you changed since last compile need compiling.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +2

      Only the purists will really complain about header-only code! Your Real World experience shows that it still works. 👍🏻

  • @stevex3976
    @stevex3976 ปีที่แล้ว

    Hi Ralph, I used your video as a guide on a program I have been working on for a friend. It uses a Nano with a 2 x 16 LCD to read and display the position of a volume pedal on a guitar effects stomp box. User can select between 2 display types. I ended up with 2 function calls in the main loop! Thanks for the direction!
    void loop()
    {
    twoBar = CheckUserInput(twoBar);
    DisplayPedalPosition (twoBar);
    } // end loop

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      Fantastic! The more you use the technique the better it becomes.

    • @stevex3976
      @stevex3976 ปีที่แล้ว

      @@RalphBacon Now with namespace!
      void loop()
      {
      twoBar = UserIn_NS::CheckUserInput(twoBar);
      Math_NS::DisplayPedalPosition (twoBar);
      } // end loop

  • @steverileyretired
    @steverileyretired 2 ปีที่แล้ว

    Good idea

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      Many thanks! Now go and try it, Steve, with some simple project you've previously written. It really makes a huge difference the more you do it this way.

  • @robertmurphree7210
    @robertmurphree7210 ปีที่แล้ว

    as retired C programmer, I didn't want to do any fancy C/C++ macro, or class stuff as an arduino hobbyist, afraid it would cause problems in arduino compiler. Introducing multiple files and hiding detail to hobbyist is great, I like just using .H files instead of .h + .cpp for hobbyist. and use of namespace to hide detail in multiple files suits hobbyist much better compared with lots of other C++ fine grained object oriented features. Enjoy your video's. great Teacher once told me his philosophy was to make things so easy people are comfortable enough and can use them as tool for life. Robert Murphree, retired state of Oklahoma programmer.

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      Great to hear from you Robert! Old programmers never retire, they just decompile 😟😲😉😁

  • @haraldh.9354
    @haraldh.9354 2 ปีที่แล้ว

    good work -thx

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      No problem 👍

  • @ChristianSchmid
    @ChristianSchmid 2 ปีที่แล้ว

    Maybe a gel like from LEE filters over jour display may increase the contrast significantly.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +2

      Good idea. I have a whole booklet of swatches from them somewhere... that's the problem. 😢

    • @geoffpanchaud967
      @geoffpanchaud967 2 ปีที่แล้ว +1

      I use Quality street wrappers as coloured filters! The wrinkles hardly notice in practice.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      I've done that! Really! The purple ones really work well! Warm them in hot water to take out the crinkles.

  • @jeffbluejets2626
    @jeffbluejets2626 2 ปีที่แล้ว

    Hi Ralph, Think I saw something about that "read only once" written as an ifdef ....??? no...??
    found the library upgrade problem also myself in the past. Some code writers will have a particular library included in the code.
    One can see it in the extra tabs which I thought a good idea.
    Maybe, instead of updating libraries, they should simply add a newer version, leaving the older versions in place with particular version number referenced in ones code.
    Had pc hdd die a couple of months ago and since found out, due to new install of IDE required, all my libraries have reverted to the "basic" as IDE installed.
    A right PIA as even the LCD library you refer to, I had to chase up and install. Then naturally check against some of my older code to see if it stll works.
    Bound to be some that will catch me out again in the future.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      That's just one reason why I use VS PlatformIO; you can add libraries to the project and they will _never_ get updated. Just the way I like it. If you _do_ want libraries to update to a specific version then it will do that too, of course.

  • @Hangs4Fun
    @Hangs4Fun 4 หลายเดือนก่อน

    question, at 13:46 you show using the printf function in your lcdHelper.h file, but you don't have any reference to the printf library at the top of that file. Is the compiler finding the function somewhere through the daisy chaining of includes and such? Great video's btw :-)

    • @Hangs4Fun
      @Hangs4Fun 4 หลายเดือนก่อน

      and as I watch further in, at 14:36 I see the LibPrintf.h include in the globals.h file... I remember that from one of your earlier video's, just didn't know the includes worked that way, thanks.

    • @RalphBacon
      @RalphBacon  3 หลายเดือนก่อน +2

      In this case, including a library at a high level ensures other parts of the code can find it (if those files are included too). So, yes, one reference to the library is enough, which I often put in the globals.h if it is a "general" library (ie used by lots of sketch files) and globals is then included in other files.

    • @Hangs4Fun
      @Hangs4Fun 3 หลายเดือนก่อน

      @@RalphBacon thanks, much appreciated..

    • @livetohash6152
      @livetohash6152 หลายเดือนก่อน

      ​@@RalphBacon... Again with a Q that I could test, but would it be good to put your compile directives in a global.h file for easy reuse? Like #define debugln(x) Serial.println(x) and others? Then you can erase them to save space later🤞

  • @livetohash6152
    @livetohash6152 หลายเดือนก่อน

    I'm curious, if you use nice names in your code, could you compress that for deployment?
    Like instead of pirForward pirWest and pirBackyard...
    Do #define pirForward p1... Etc...
    Would this help save space?
    .. I could just test it, but curious your thoughts 🤔

    • @RalphBacon
      @RalphBacon  หลายเดือนก่อน +1

      If you had a variable called 'my_Exteremely_Long_Name' and another sketch with a variable called 'x' which would compile to the smallest code?
      The answer is that they would both compile to the same size, because variable names are ONLY used at compile time, not at run-time (ie post compile/link stages).
      Just in case someone jumps in to correct me, if you compile in debug mode (rather than release mode), yes the long names are retained for debugging purposes (how else would you reference a compiler variable name of y21?). But in general terms, variable names do NOT increase code size. You can try this for yourself pretty easily!

    • @livetohash6152
      @livetohash6152 หลายเดือนก่อน

      @@RalphBacon thanks, you rock 💯👍

  • @gregorymccoy6797
    @gregorymccoy6797 ปีที่แล้ว

    Good practices. Gosh if only some of the libraries were as well-written. We have rigid style practices and project layout at work for code and some of them are senseless... But at least we have them 🙂.

  • @bryansmith1303
    @bryansmith1303 ปีที่แล้ว

    All your videos help me be a better programmer. In the download file, in the sensor helper file. In the code copied here:// Read sensor
    float getTemperature() {
    sensor.requestTemperatures();
    Serial.println("Requesting temp");
    while (!sensor.isConversionComplete())
    ;
    a++;
    // Get temperature (float)
    return sensor.getTempC();
    }
    You are using a++; I can't fine where is it declared and what is it used for. Could you explain?

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      This is odd, Bryan.🤷
      Not only do I not know what that is doing there, I would almost never use a silly variable name like "a".
      Delete that line and pretend it never happened. I'll try and update the GitHub file too.
      Have I been hacked?

  • @jcxtra
    @jcxtra 2 ปีที่แล้ว

    Small comment this week (I think, at least that's my intention!) Could you do a video on how to setup VS Code to do arduino projects? I like some of the ways that VSCode works but the guides I've seen online haven't been easy to follow. I started using an IDE for a Tcl project I worked on (Komodo since it can do contextual things as it understands the language and has links to git) and I think I'd like to set that up since eventually other people are gonna have to be able to work with my code too, so that'd be a nice video, if you wouldn't mind ^^, Pretty pweese? (After you've done pointers and structs :p) x

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      Demanding or what? 😁 Actually, the new Arduino 2.0 IDE has the potential to be really good although whether it becomes as good as VS (PlatformIO) time will tell.
      I'll wait for the final release of the Arduino IDE first before committing to a VS video as it's going to open up Pandora's box, I just know it is.

  • @aleksanderklimczyk7879
    @aleksanderklimczyk7879 2 ปีที่แล้ว +1

    How would you replace: String newStr = str1 + str2 + str3; To make it use less memory and combine all the strings into one at once?

    • @jimbarchuk
      @jimbarchuk 2 ปีที่แล้ว

      In globals you define various variables and defaults. In sensors add code to read into variables, or other code to create them. I don't recall exactly what the details of LCD code here was, so what I describe is generic... After all mainline functions are done, there's an 'end of loop' function to concatenate all the variables. Then LCD prints just one variable.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      @Aleksander I try to avoid the String type (capital S) and use the std::string type but that is probably only available on the ESP32.
      In the Arduino, one way of concatenating Strings 'efficiently' is to do something like this:
      String a = F("Hello ");
      String b = F("World");
      String c = "!";
      a.concat(b.concat(c));
      And so on.

  • @ianbertenshaw4350
    @ianbertenshaw4350 2 ปีที่แล้ว +1

    This is really something that you do with bigger programmes , newcomers need to understand that the simple programme you showed isn't something you would be bothered with .Handy to know how to do it for sure but for something less than say a hundred lines of code probably not worth it .

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      As I said, Ian, projects tend to ☘ grow. If your code is a mess before it starts to expand, well... you've got a problem! A small program like this probably doesn't need the full treatment but it's a good place to start (and practice).

  • @markvreeken
    @markvreeken ปีที่แล้ว

    Question ; My standard library files don't include #pragma once . Is this just sloppy programming or ????

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว +1

      They might include the more "traditional" way of preventing multiple inclusion by using something like:
      #ifndef __SOME_NAME
      #define __SOME_NAME
      all the library stuff in here
      #endif
      But "#pragma once" is the new, improved, time-saving way of doing it.
      One or the other should be present, otherwise it is, indeed, sloppy coding! 😁

  • @josip1881
    @josip1881 2 ปีที่แล้ว

    Did you try to use Rust? :) Much easier to organize, but lacks some libs, nothing too bad.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      I've not used Rust, although I hear it's good for embedded processors. In case others want to find out more, here's the link:
      www.rust-lang.org/

  • @PhilipHawthorne
    @PhilipHawthorne ปีที่แล้ว

    Hi Ralph. I've been using multiple files for some time in large projects but in some of the files I gave the .ino extension and some are .h. It seems to work ok but is this bad practice to use .ino? I have to #include the .h files but not the .ino. I assume the Arduino IDE is doing some work in the background?

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว +1

      Well... if it works for you🤷... But seriously, that's not exactly _standard_ naming practice. Headers (definition etc, no code) should have the ".h" extension, code (C++) should have the ".cpp" extension and if you do everything in the one file it should have the ".hpp" extension.
      But I break my own rules so... back to my first sentence! 😉

    • @PhilipHawthorne
      @PhilipHawthorne ปีที่แล้ว

      @@RalphBacon Thanks Ralph. I haven't been able to get PlatformIO working in Visual Studio Code properly so I'm using the new Arduino IDE 2.0.1. I do tend to put definitions of constants etc in .h files and the .ino files contain the code. Does the new IDE accept .cpp files? They're the same thing really, I suppose! As it's working, I will probably leave well-enough alone 😊😁

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว +1

      If it ain't broke, don't fix it! 😂

    • @PhilipHawthorne
      @PhilipHawthorne ปีที่แล้ว +1

      @@RalphBacon Yes, as my Dad used to tell me 😂

    • @1over137
      @1over137 ปีที่แล้ว +1

      @@PhilipHawthorne The INO files are the input for the Arduino IDE's pre-processor that adds all the scary code they don't want newbies to see. Also they don't want newbies to suffer from the frustrating fact that C++ is picky about the order of definitions and usage. If you define a function at the bottom of the CPP file, you can't call it in the functions above. For that you need to either move it to a header (exported variables for other modules to see) or a forward declaration at the top of the file.
      If you wanted to move away from the Arduino IDE, your first task will be to convert your INO files to actual CPP files. It's not hard, but you will start to face a few more elements of C++'s fussiness and somewhat frustrating error texts.

  • @jstro-hobbytech
    @jstro-hobbytech ปีที่แล้ว

    Ralph I wonder if there's a place for the 1 line either or expressions based on a condition like.
    Ralph ? Awesome : supercool;
    I forget what it's called haha. Most things we do I find can be broken down into pure boolean/bitwise operators to return a value that can trigger related logic or just keep on boogying through the code haha. By bitwise I don't mean the shifting unless you set up an 8 bit counter that decrements or otherwise every second check. Like when we go some stupid sensor to check

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว +1

      I love that ternary expression, Joey!

    • @jstro-hobbytech
      @jstro-hobbytech ปีที่แล้ว

      @@RalphBacon I cracked myself up when I was typing it hahaha

    • @jstro-hobbytech
      @jstro-hobbytech ปีที่แล้ว +1

      @Ralph S Bacon I've become a member haha. I thought I already was. My apologies

  • @fillempie1501
    @fillempie1501 2 ปีที่แล้ว +1

    Why do you put all the sourcecode in the *Helper.h" file? It is more common to place the function-interface (name of the function and parameters) in the .h file and the sourcecode in the cpp.file. Also after you changed the cpp file the whole project does not need to recompile....

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Hmm, I knew some developers would object to this, hence my brief explanation in the video that for hobbyists it's probably "good enough" and allows beginners to understand the concept of code modularisation without the added complexity of multiple headers and multiple matching cpp files. It can easily overwhelm a beginner who is trying to separate concerns and adhere to the single responsibility principle.
      But if you want to do it 100% correctly, we should indeed put executable code into cpp files and only declarations into header files.

  • @1over137
    @1over137 ปีที่แล้ว

    The rant around 13mins. At times I have agreed. However. The pain it will cause you later is far too great and far too much hassle to clean up. So I would advice at least learning/showing how to do it properly. Because "header only" C++ files have to be done very, very carefully or they will bite you. In your example, the instant you need to include the LCD header into more than one place your code will fail due to multiple definitions (and allocations) for LiquidCrystal_I2C lcd() object. Note "#pragma once" means once PER file. If you have a.cpp and b.cpp and both include your lcd header, BOOM! If you want header only C++ files YOU CANNOT HAVE GLOBALS in it. You either have to move your allocation declarations to the parent CPP file, create an actual hpp/cpp pair or you can wrap the allocation inside the class and allow it to allocate it's own memory dynamically (so it's not global).

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      There does need to be some severe structure to header files: what's in this, what files _they_ include, and what variables they need.
      I put globals into a separate file (usually). Mostly with a namespace. That way it can be included wherever it's needed and won't be included twice, even if header "a" includes it as does header "b".
      I've recently started to use ".hpp" files, purely because it's Best Practice. Makes no difference to the compiler or anything else. C++ lets you hang yourself if that's what you want to do!

  • @zyghom
    @zyghom 2 ปีที่แล้ว

    nice usb cable from the well known product ;-)

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      I can't remember where that yellow (and very flat) USB lead came from. Probably some audio product? But it is very durable and I keep using it!

    • @zyghom
      @zyghom 2 ปีที่แล้ว +1

      @@RalphBacon Ultimate Ears Boom Bluetooth Speaker

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Oh yes, the one I gave away to my daughter who now refuses to return it. I remember. 😢

    • @zyghom
      @zyghom 2 ปีที่แล้ว +1

      @@RalphBacon I am not surprised - I ended up with 4 in my house - everybody loves them!

  • @sledgex9
    @sledgex9 2 ปีที่แล้ว

    Also, maybe, instead of "#define ONE_WIRE_BUS 8" do either "static constexpr uint8_t ONE_WIRE_BUS = 8;" or put into an anonymous namespace like so:
    namespace {
    constexpr uint8_t ONE_WIRE_BUS = 8;
    }
    Both of those restrict the scope of ONE_WIRE_BUS to that particular file and provide type safety. Modern compilers will optimize away the variable.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      Yes, that is a good way to do it too. From a compiler viewpoint I don't think it makes any difference whether we use a #define literal or a constexpr but either way is fine for me.

  • @yogeshitaliya473
    @yogeshitaliya473 2 ปีที่แล้ว +1

    😍😍😍

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      I don't know how you are always the first, Yogesh. well done 👍🏻

    • @yogeshitaliya473
      @yogeshitaliya473 2 ปีที่แล้ว +1

      @@RalphBacon respect sir
      Your all video 😅😍😍😍

  • @zyghom
    @zyghom 2 ปีที่แล้ว

    if you included globals.h in the main file, then calling it again in lcdhelper file has no meaning (considering pragma once), right?
    with regards to have functions in h files: some libraries - even huge ones - are completely in h files - see i.e. ArduinoJSON library: "ArduinoJson is a header-only library, meaning that all the code is in the headers."

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      Including the globals.h in more than one file allows the IDE to be satisfied that each file can access stuff in the globals.h (whilst you are developing) but it will not include the file more than once during compilation as that would introduce duplicates, of course, just as you have understood.
      I never knew that some libraries were written with just a header file. I guess they were too busy to split it out!

    • @zyghom
      @zyghom 2 ปีที่แล้ว

      @@RalphBacon if I am not mistaken (I might be of course) the code can be in txt or csv files - it is just a matter of convention, right? also splitting function declaration and function definition: as long as declaration is before definition... but what do I know ;-)

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว +1

      I've never tried to #include a .csv file! Perhaps it would work, but it would be very 🙄confusing. Best to stick with convention in this case, Zygfryd!
      Yes, declarations must come before definition but if your definition is written in a file before that you #include elsewhere then you don't need to (forward) declare anything.

    • @zyghom
      @zyghom 2 ปีที่แล้ว

      For my home projects I have built a library where most of the functions are in separate files, i.e. wifi.h, pwm.h, mqtt.h etc. This way, by using #define in the main file and then #ifdef in the further ones, I can include some modules in the project, depending on what is the device for. I.e. if the device is to use SHT31 humidity/temperature sensor, I use #define USE_SHT31 and then all is done. The idea was to avoid rewriting the projects from the scratch but also, whenever I improve/change my code, I make a change in 1 file only (i.e. I recently changed wifi function to use fixed channel) - this way I change 1 file only and perform recompilation of all my devices (recently 50+ esp32 devices in my home) and the OTA to all of them (btw I made OTA from web so just 1 press on the home assistant screen to update them all at once).
      I wish I could find the way of re-compiling 50 projects at once instead of opening all 50 in Arduino... Maybe one day I will to to platformio but not yet.

    • @zyghom
      @zyghom 2 ปีที่แล้ว

      ... and I just keep 1 config file per device i.e. configure.h where there are device specific settings, i.e. the name or fixed IP address etc.

  • @TheOleHermit
    @TheOleHermit 6 หลายเดือนก่อน

    Thank you so much for covering these 'tips of the trade', Ralph.
    You described me to the 'T' as being a self-taught, untrained coder. I've learned 95% of what I know from YT tutorials, like yours, but have never seen anyone else covering these finer details, like passing references to memory.
    My current project is a MIDI controlled Teensy 4.1 laser synth, with ~1500 lines of code in my Arduino sketch... so far. I've only just come to the point of learning how to create my own much needed custom libraries.
    "Necessity is the mother of invention."
    This video greatly simplifies how to get from A>B>C++.
    Excellent series of shorts. 👍

    • @RalphBacon
      @RalphBacon  5 หลายเดือนก่อน

      Glad its helped you make your code sleeker!

  • @maduromaduro9826
    @maduromaduro9826 ปีที่แล้ว

    creating many files is not a kind of "overhead" ? I am asking for high performance computing not embedded

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      There is _zero_ overhead in creating many files. The compiler will always create a single file containing all the referenced files (including libraries) and then compile.

  • @ipadize
    @ipadize ปีที่แล้ว

    3:15 that text
    me: if if if if if
    programmer: Thats literally unreadable!
    me: i can read that perfectly 🤷‍♂

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว +1

      If I put my glasses on I can read more things, for sure!

  • @TheEmbeddedHobbyist
    @TheEmbeddedHobbyist 2 ปีที่แล้ว +6

    Noooooooooooooooo, Headers are interfaces to the the code. No code in the header files please. 🙂
    Sorry just had to say that, so when pep's look at headers and find there is no code they might look for files with the same name but diffent extension. As you say .cpp
    I'm just old school. well old and left schol a long time ago 🙂

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Old school is not a bad thing. At work we would have to do the "right way", of course.
      I knew some developers would object to this, hence my brief explanation in the video that for hobbyists it's probably "good enough" and allows beginners to understand the concept of code modularisation without the added complexity of multiple headers and multiple matching cpp files. It can easily overwhelm a beginner who is trying to separate concerns and adhere to the single responsibility principle.
      But if you want to do it 100% correctly, we should indeed put executable code into cpp files and only declarations into header files.

    • @markday3145
      @markday3145 2 ปีที่แล้ว

      No code in header files... unless you need/want to inline something. 😉
      For a small hobby project, being written by someone who isn't a professional programmer, putting project-specific code (wrappers, convenience functions) in a header file isn't THAT bad. You're just splitting up that one big monolithic source file into a few files to make finding things later a little easier. Because of the way the Arduino IDE (or PlatformIO) works as a build system, they have to be header files, not .cpp files.
      If the code is more general, and you want to be able to reuse it later (even from multiple source files in the same project), then putting declarations in a header file and definitions/implementation into a source file makes more sense. You can always do that if/when you get to the point of wanting to reuse the code.
      Having used other languages that don't separate declarations, the C/C++ way starts to feel cumbersome and error prone. The one big advantage of the C/C++ way is that you can distribute precompiled libraries without source, in a way that is compatible with just about any compiler (as long as they understand the object file format, and can use the same ABI).

    • @peut
      @peut 2 ปีที่แล้ว

      You can just add tabs and call the file .ino. Arduino will 'glue' them together at compile time. No need to include them and no code in .h files.

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      But if you don't #include them, then the declarations are not visible to the IDE for other programs to refer to?

    • @peut
      @peut 2 ปีที่แล้ว

      @@RalphBaconthey are visible, as Arduino concatenates all .ino files before the compile (apparently)

  • @Tanuki5
    @Tanuki5 ปีที่แล้ว

    what about a .hpp file?

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      Interesting you should bring that up. Certainly, from a pure "C" perspective there is no indication with a ".h" file that the contents are "C" or "C++" whereas ".hpp" unambiguously states this is a C++ header (with or without code).
      Up to you whether you want to follow this methodology (Boost uses it) and it might makes things clearer to C programmers but C++ programmers might feel they don't need it.

  • @stephenbarlin2314
    @stephenbarlin2314 ปีที่แล้ว

    I tried this and just ended up with unfathomable scoping problems.

    • @RalphBacon
      @RalphBacon  ปีที่แล้ว

      Remember you must refer to each function and variable in a namespace by its namespace followed by two colons. Eg myNs::myFunction();

  • @browaruspierogus2182
    @browaruspierogus2182 2 ปีที่แล้ว

    good programmers don't use Arduino framework lol

    • @RalphBacon
      @RalphBacon  2 ปีที่แล้ว

      Beginner programmers do, though. 😜