Electronics And C++ Education With An ATTiny13

An ATTiny board that one of the students developed for this project, etched on single-sided FR4.

When [Adam, HA8KDA] is not busy with his PhD studies, he mentors a group of students interested in engineering. To teach them a wide range of topics, he set out to build a small and entertaining embedded project as they watch and participate along the way. With this LED-adorned ATTiny13A project, [Adam] demonstrated schematic and PCB design, then taught C++ basics and intricacies – especially when it comes to building low-footprint software – and tied it all together into a real-world device students could take home after the project. His course went way beyond the “Hello world”s we typically expect, and some of us can only wish for a university experience like this.

He shares the PCB files and software with us, but also talks about the C++20 framework he’s developed for this ATTiny. The ATTiny13A is very cheap, and also very limited – you get 1K of ROM and 64 bytes of RAM. This framework lets you make good use of it, providing the basics like GPIO wiggling, but also things like low-power operation hooks, soft PWM with optional multi-phase operation support and EEPROM access. Students could write their own animations for this device, and he includes them in the repo, too!

In educational projects, it pays to keep code direct and clean, cruft-less and accessible to students. These are the things you can only achieve when you truly understand the tools you’re working with, which is the perfect position for teaching about them! [Adam] intends to show that C++ is more than suitable for low-resource devices, and tells us about the EEPROM class code he wrote – compiling into the same amount of instructions as an Assembly implementation and consuming the same amount of RAM, while providing compile-time checks and fail-safe syntax.

We’ve talked about using C++ on microcontrollers before, getting extra compile-time features without overhead, and this project illustrates the concept well. [Adam] asks us all, and especially our fellow C++ wizards, for our opinions on the framework he designed. Could you achieve even more with this simple hardware – make the code more robust, clean, have it do more within the limited resources?

What could you build with an ATTiny13, especially with such a framework? A flashy hairclip wearable, perhaps, or a code-learning RF-remote-controlled outlet. We’ve also seen a tiny camera trigger for endurance races,, a handheld Flappy Bird-like console, and many more!

28 thoughts on “Electronics And C++ Education With An ATTiny13

  1. Interesting, and I have to check into the details. Going for the minimum.
    Burkhard Kainka did a Tiny13 project called Sparrow – programming the Tiny13 via sound.
    https://www.amazon.co.uk/Cheepit-Sparrow-Programming-Mobile-Phone-ebook/dp/B074G54KRR
    some free stuff
    https://www.ak-modul-bus.de/cat/documentation/Sparrow_Manual_English_v6_Partial_A5.pdf
    and a lot on his website
    https://www.elektronik-labor.de/AVR/Sparrow/Cheepit.html
    I got a few of those and built the Minimum System as well to try

    1. Maybe we can replicate Hondo’s arrow by writing a ATtiny brainf*ck interpreter, assigning whistles to the symbols, and using it to control that stick drone from a few days back. :-D

    2. I think this mini-embedded, highly constrained exercise would not be a good way to teach a beginning student programming with C++. The topic is just too specific, and lacks the necessity for exposing a lot of C++ features. However, this course might be ideal fit an otherwise-experienced programmer (say, coming from JAVA or Python) to see the real power of the control one gets over native object code generation. C++ is potentially even better than C for this task, because it has a rich set of qualifiers that allow the programmer to communicate to the compiler precisely the guarantees in place with the code. This in turn frees the optimizer to brutally edit the object code. Of course it also has attractive features from modern C, like memory alignment, in-line assembly, and wonderful pointer arithmetic. These are important to the embedding process and IMHO strike a perfect balance between human-readability, platform independence, and down-to-the-metal control of the resulting object code. These are things that are foreign to JAVA, JavaScript, and Python programmers, so you’d be actually adding to their programming toolbox.

      Also, this information is absent for the most part in modern computer science curriculums, and you’d be doing them a favor. Compiler design is absent as a requirement in many degree programs, so the idea of stack frames, and basic correspondences between programming patterns in source code are missing concepts, even if the coursework is in a compiled language like C++.

      I have an anecdote that illustrates this from my time doing Mac OpenGL drivers (in C++) for processor graphics at Intel. One of my younger co-workers asked me to look at a crash he was experiencing in code that was algorithmically correct. The problem was that he was creating enormous data structures on the stack as local variables. Theoretically, from a language standpoint, it seemed like a good way to manage these transitory buffers, but he was blowing out the stack when only a few layers deep in function calls. I told him he had a segmentation fault from accessing memory below the stack from creating huge objects in his local stack frames, and he looked at me blankly. He knew how everything in C++ worked from a language standpoint, but he had no idea how function calls were implemented in object code. He was a smart person, experienced in using C++ for general programming, but he had never run into something that required understanding the limits of the target platform before, and he was helpless. Using the mini project, you would be shining a bright flashlight on these issues, and on how to manage such issues in C++. This is the arena where it really shines even over other compiler languages.

  2. While it’s laudable to learn to control the bloatiness that C++ tends to provide, and nothing’s going to drive that better than trying to fit into a Tiny, I’m not sure introducing students to embedded using C++ at this scale is the best path, not without caveats at least.

    Giving the beginners the experience that, yes, they can do hardware, yes they can learn C++, and yes they can fit C++ into constrained environments is all good. C++ shines when you have need of lots of instances of things, and there are many embedded environments with resources enough to support that. In cases though, where there aren’t a multiplicity of objects, and where h/w resources are short, C++ is generally not a great fit.

    As an academic exercise, sure, cram some C++ into 1K ROM and 64 bytes, with the knowledge that you’re swimming against the current. Know that straight C is a much better fit for platforms of that size, but take the lessons learned from fitting C++ to it, and apply them to larger systems, and you will do well. Someone who has only ever programmed C++ on a commodity PC might look at an embedded platform with, say, ‘only’ 1M of ROM and 256K RAM and recoil in horror, but if you’ve fit it to a Tiny, that’s going to feel luxurious, and there are many such environments, and problems for them that do want what C++ has to offer.

      1. A skilled FORTRAN programmer can write FORTRAN in any language.

        You can clearly write C in C++.
        I think all real world C++ supports inline assembler. Not sure if that’s in a spec, violates a spec, neither or both.

          1. It’s not going to be portable and is certainly a potentially ‘premature optimization’.

            My point is that C++ code does not have to be an awful object onion. You can write FORTRAN, C or assembler in most flavors of C++.

    1. The trope that C++ is bloated and C is lithe and small has been debunked more than 20 years ago. Let it go and get with the times.
      The truth is that most embedded C nowadays is written by EE’s with very little experience in programming anything and even less incentive to write efficient code. The amount of inconsistencies and code duplication I’ve seen in BSP’s from “respectable” chip suppliers is horrifying.

    2. >I’m not sure introducing students to embedded using C++ at this scale is the best path, not without caveats at least.

      What’s the second, companion platform to learn C++ on? Assuming you tackle C++ on two platforms at once, progressively through their learning materials, I mean.

      Couple “this” with a bigger embedded system (STM etc), or “this” with learning C++ on a full-blown desktop OS?

  3. Templates are part of the bloat problem. The compiler instantiates lots of functions for several different types although the functions often end up doing exactly the same.

    1. perhaps with a bad toolchain. I personally remember a Sun WorkShop compiler generating a 20 MB binary whereas a MSVC compiler generating a 3 MB binary. Looking at the map file, there were indeed multiple redundant copies of template expansions. But is that the language’s fault, or the toolchain? (and this was 20 years ago, so I’m sure their product has gotten better).
      Linkers will (or should) factor out identical instruction sequences irrespective of their symbolic name. Some will also factor common function tails, but ultimately these optimizations are the toolchain vendor’s job (and competitive advantage) rather than an aspect of the language design. Although modern C++ has added a bunch of language features that make it easier to generate even more efficient code by conveying programmer intent that a compiler cannot otherwise assume on it’s own.

      1. no linker will factor out identical sequences with different names.

        but yes, the linker is really where a lot of C++ template magic goes to die. in some sense, they’re capable of a bit of subtle magic but for the most part linkers are dumb as rocks, even ‘modern’ or ‘advanced’ ones. the real advances have been in new object formats (different uses of ELF section names and so on). if the compiler puts each function in its own ‘weak’ section then the linker can do a good job on some of the redundancies. if it doesn’t, it can’t. it’s not really a question of whether the compiler is “good” or not, just which format it uses.

        g++ and clang++, have tended towards using appropriate encodings on most platforms and by now just about anywhere you can use them, you’ll get a decent job. now the most typical overhead is the compiler will generate an enormous amount of redundant and unused code, and then the linker throws it all out. it’s not uncommon now to have 400MB of objects go in and a 20MB executable come out.

        there are ways around that one too but so far as i know they are still pretty uncommon in real life. they require a little different compiler / linker invocation pattern than is typical (i.e., different Makefiles or whatever).

        of course, you throw in something like shared libs (not on attiny, thank god), and there’s a whole new realm of insurmountable problems. and they truly are insurmountable, the whole system becomes bloated inevitably and there’s *almost* nothing you can do about it (except, of course, being careful about which subset of C++ features you use). the worst part is that the whole thing becomes very brittle, and hard to diagnose.

        but even with all of that, even when it’s all working perfectly…jeesh C++ makes it very easy to generate a ton of useless code unintentionally and no matter how good your toolchain is, that code is gonna take up memory at runtime. i’ve heard a lot of different C++ evangelists over my time say that the problem is that people don’t know how to use C++. and it’s true, if you are very careful in coding your C++ you really can come up with exactly the same efficiency as C. but i’ve on occasion asked them to put their code where their mouth is and they’ve always shown that they, seasoned C++ evangelist, too, are surprised by what the code they write actually means. surely there is a true scotsman out there but i haven’t met him.

        1. You’ve said most of what I was thinking.

          The main problem with a C++ framework for a microcontroller like this is that desktop C++ programmers are going to pick it up, write a heavily object oriented program that uses all of C++’s special features, and then they won’t understand why they “simple” program won’t fit and uses more ram than the chip has.

          This is a problem I see on mobile software all the time. I have a pretty nice medium end tablet, but half of the software is really laggy on it. It’s not the stuff you expect though. The big MMOs with impressive graphics and 3D rendering work fine. It’s the simple block breaker or Galaga style dropper, designed to help you learn another language, that is too laggy to play. And it’s not always the language either. I mean, you can tell a studio is doing their due diligence when their games are written in C++, but even some of the complex Java games do manage to perform well with high rendering and physics loads.

          So the problem isn’t going to be the embedded systems people who pick it up and program it in C++ (though most of them will just go for C, because it’s often legitimately easier for embedded tasks). The problem is going to be the inexperienced people who don’t know C and who need the framework, because they don’t have any experience with embedded devices. They are the ones who will pick it up and try to make a simple controller for something using tens of objects, complex data structures, and so on, and when it won’t even fit on the flash let alone run on the limited memory, they won’t know how to fix and optimize it either. Basically, it sounds good, but it’s not going to help the target audience at all, because they’ve grown up in a world where, “It’s fine, because modern computers have practically unlimited memory!”

          The truth is, carefully contrived examples written in bloated languages can often perform just as well (or at least close) as the same task in a more optimized lower level language, but that never represents most use cases and almost never practical use cases. It’s a cool exercise to figure out a use case where you can make C++ work on 1kb of ROM and 64 bytes of RAM, but just like they guy that managed to find a case where Java performed as well as C++, it just doesn’t scale. As soon as anyone tries to use this for a real world application with real world C++, it’s going to be a nightmare, and the guy who does it won’t have the skill to fix it, because any experienced embedded systems engineer is going to use C for real world applications.

          (To be fair, this is coming from someone currently trying to make a musical instrument/synth in CircuitPython. This isn’t something I plan to mass market though, and if I was planning on doing that, I would totally be using C, because it would be a waste to make a $10 device using CiruitPython, when I could use C instead to add enough additional capabilities to sell it for $100 a pop.)

  4. gah i feel like i’m being trolled. C++! on an attiny! on an atmega, sure, there’s a use case for a higher level language. the stm32 is so powerful that maybe using it mostly for I/O-oriented things like i’ve been doing is even stupid, maybe you should only use it for programs with lots of logic that could use a high-level language.

    but the attiny!!! what!!!

    1. What makes you think that C++ is not appropriate for a resource constrained microcontroller and C is?

      It is sad that people keep repeating this unfounded myth, while Godbolt has proven they are misguided for almost a decade!
      Don’t repeat hearsay. Test your assumptions at Godbolt.com and compare actual generated binaries.

  5. You do not need to use OOP or Templates in C++. I have worked with c firmware development for years and was convinced c++ was horrible for resource limited systems. Then we got a new guy in who proposed we switch to c++, i laughed at him ^_^. Then he got back to me with actual code examples that showed real improvements we could make in our codebase that was better then our c code… (size and code clarity)… Have not looked back since.

    To quote a Stroustrup paper:

    “C++ directly supports a variety of programming styles. In this, C++ deliber-
    ately differs from languages designed to support a single way of writing pro-
    grams. This paper briefly presents key programming styles directly supported by
    C++ and argues that the support for multiple styles is one of its major strengths.
    The styles presented include: traditional C-style, concrete classes, abstract
    classes, traditional class hierarchies, abstract classes and class hierarchies, and
    generic programming”

    https://www.stroustrup.com/oopsla.pdf

    1. Dan Saks talks about the same problem you describe. Not a lot of other folks are, however. At least not in a format that’s attractive and actionable by decision-makers.

      I’m a non-systems programmer/tester working in the embedded router space. All of the systems developers here work in C not C++. I’m presently learning C. I’m no authority on any of this, but curious since I have a hobby interest in 6502.

  6. I need more keys
    yes/no/up/down == 4
    I need information (8 segment number or 8 diodes==256)
    and power on board (lipo, AA or everything from 0 to 12V my prefer!)

  7. I’m late to the party but here goes. My career started with C++ (Visual C++ and Rational Rose; 1997 – 1999) but I worked my way “down” to embedded bare metal C and haven’t found a compelling reason to go back so far BUT…

    There are some really good arguments for C++ If you want your code to be easy to read, maintain and extend by other people or yourself if you suffer from Alzheimer’s lite like me :)

    I’m guessing some of you already do a form of lite object orientation already in C? Here are some of my personal examples (not my intention to plug; just the quickest examples that come to mind):
    – [Safe string appending Buffer](https://github.com/piconomix/px-fwlib/blob/master/utils/inc/px_sbuf.h)
    – [Ring Buffer](https://github.com/piconomix/px-fwlib/blob/master/utils/inc/px_ring_buf.h)
    – [Software Timer](https://github.com/piconomix/px-fwlib/blob/master/utils/inc/px_systmr.h)

    It IS possible to implement inheritance and polymorphism in C but C++ has syntactic sugar that hides the extra hoops that you have to jump through. The great [Miro Samek](https://www.state-machine.com/video-course) has an excellent set of course videos on it:
    – [#29 OOP Part-1: Encapsulation (classes) in C and C++](https://youtu.be/dSLodtKuung)
    – [#30 OOP Part-2: Inheritance in C and C++] (https://youtu.be/xHMje9fL1Bk)
    – [#31 OOP Part-3: Polymorphism in C++](https://youtu.be/xHMje9fL1Bk)
    – [#32 OOP Part-4: Polymorphism in C](https://youtu.be/2v_qM5SJDlY)

    Function overloading is a great zero overhead feature in C++. Where it gets tricky for me is when operators are overloaded… it’s not always intuitive for the uninitiated.

    The trap / pitfall is trying to create a C++ class that’s future proof. You spend way too much time trying to cater for every conceivable use case scenario and invariably fail.

    So when a project scales above a certain point then C++ may be a better way to manage the complexity but I haven’t hit that ceiling yet. I try to stay on the low end (128k flash or less).

    P.S. My last exposure was C++ 98 and I’m sad that I haven’t kept up and know what C++ 20 has to offer. This article and comments are great, thanks!

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.