Writing The Software

August 14th, 2017 8 min read

Now that the hardware part of my Electrocard is done, it’s time to start working on the software side of my electronic business card.

First of all, it’s good to make a list of the desired functionalities. Of course, the OLED screen combined with the 3 push buttons give me a lot of nice opportunities, so most of all, the display will be used to display the important company information for as far as it isn’t already printed on the PCB’s.

Secondary, it would be nice to display some debug information regarding the battery, the memory and the software version.

And last but not least, it’s mandatory to add a nice easter egg to a business card like this. But more about that later.

The controls

image

Since I have three buttons, and have both primary and secondary functionalities, I want to add a short press and long press feature to the buttons. This is easily done with the help of the clickButton library. It gives me a simple way to add 6 desired modes:

  • Button A, Short press: Power on / Display the startup screen.
  • Button B, Short press: Display my company’s address.
  • Button C, Short press: Display my company’s & blog’s url.
  • Button A, Long press: Standby mode (also initiated after 30 seconds of inactivity).
  • Button B, Long press: Debug mode.
  • Button C, Long press: Start the easter egg.

With the help of the earlier mentioned button library and a simple state machine the user can switch between the various modes, without the need of an complicated user interface.

Controlling the display

Controlling the display isn’t particular difficult, especially since there are a lot of example codes and libraries available to control the SSD1306 OLED controlled I’ve used. I’ve tried a few of them, and most of them run fine on the ATTiny85 processor. But during the process I found out there are actually two separate challenges.

  1. Control the display by sending an image to the OLED controller.
  2. Drawing realtime graphics onto the screen.

The first challenge isn’t extremely difficult. Most of the available libraries allow you to send a 128x32 pixel monochrome image (a BPM converted to a HEX values stored in the micro controller’s flash memory) to the OLED controller. It took a while to figure out how to convert the BPM to the correct values, since it depends a bit on how the library reads and writes the data. But after some googling I found a working combination of the TinyOLED library and the LCD Assistant image converter.

This is the same method as used for writing text to the OLED. In stead of one complete stream of image data, every letter has a small portion of data (6 bytes) which is read from the micro controller’s flash memory.

The second part is a bit more complicated, and that is mostly due to the limited available memory (RAM) of the ATTiny85 micro controller.

The OLED expects a sequential stream of bytes to control 8 vertical pixels per byte. The 128x32 pixel oled is built up by four rows (called pages) of 8 vertical pixels (1 byte). Each 128 bytes width. In total that adds up to 512 bytes.

image

If you want to draw lines and shapes on any position on the screen, you’ll need to create a buffer for those 4096 pixels (512 bytes) in the micro controller’s memory, to which you can draw. After drawing to the buffer, you send the full buffer to the OLED and your lines and shapes are displayed on the screen. That’s how most OLED libraries work (for example, the popular Adafruit SSD1306 library).

The problem is, that you’ll need to allocate 512 bytes of memory in you micro controller as the buffer. Since the ATTiny85 I used only has 512 bytes of RAM in total, there isn’t enough memory to allocate this buffer.

In other words: I can’t allocate a buffer. I need to render the drawing while I’m writing all the graphics data to the OLED controller. This turned out to be a BIG challenge. And while I only needed this for incorporating the easter egg, it’s something I wanted to solve.

The Easter Egg: Horizontal Micro Tetris!

So, as told, I wanted to add a nice easter egg. I already programmed Pong once. But with the 3 push switches and the width 128x32 pixel screen, i reckon Tetris was better suited. Or to be more precise: horizontal Tetris. The added benefit of tetris is that it doesn’t need a high frame rate. And honestly, seeing such a cute little Tetris game on a business card, brings a smile to most people’s faces.

Due to the size of the screen, I opted to go for Tetris blocks of 4x4 pixel squares. This way, 8 blocks will fit vertically, and there is room for 32 horizontal blocks. In total there will be room for 256 blocks.

image

The blocks that are already on screen will be stored in an array called the arenaMatrix. And since each vertical row has 8 blocks, this array will consist of 32 bytes.

unsigned char arenaMatrix [32] = {};

The currently falling piece will be stored in a 4x4 block matrix, called the userMatrix. And since since we only need 16 bits of data, we can easily store this in an integer.

unsigned int playerMatrix = 0;

In other words, for storing all the blocks, we only need 34 bytes of data. Leaving us 478 bytes for additional variables (like the score and the user’s piece position) but of course also a lot of other objects and pieces of code that’s loaded in to memory.

Since the two matrixes matrices contain every piece of information I want to display on screen during a game of tetris, the render function can “simply” lookup and calculate the bytes of data for the OLED controller on the fly. This solves the lack of the graphics buffer. Or, if you look at it from a different perspecitve: this is an alternative graphics buffer with a 1/16th resolution (since the blocks are made of of 4x4 pixels).

In any way: it allowed me to render tetris without the use of any helper methods like drawLine() or drawSquare(). It all comes down to flipping bits one by one. Which isn’t the most convenient, but did teach me A LOT about bit manipulation.

Now, of course, drawing a few matrices to the screen isn’t enough to make a playable tetris. It needs some timers to drop the pieces. It needs collision detection to check if the blocks hit an other block. It needs a way to check if a fill line is filled with blocks, and it needs a way to remove the fully filled lines.

The Code

I’d love to run you thru the code, but that would probably account for a full year of blog posts. So to make things easier, I put all my code on GitHub so you can take a look at everything that’s in there. Feel free to take a look at the Electrocard Repository.

image

If you’re not familiar with the PlatformIO folder structure, you might want to start at the entrance point of the firmware in the main.cpp file. If you’re mainly curious for the Tetris code, check out the Tetris.cpp file.

Fun fact: the full code used almost every byte of flash available in the the ATTiny85 (8KB). Of course, I could work on making the code more efficient, but everything I wanted to be in there is in the code.

Game Over

And with this information, I think this project is a wrap. It was an extremely satisfying and fun project to work on. Both the electronics and the code.

Although I’m extremely satisfied with the end product, there are some things to reconsider if I start a similar product:

  • Consider a different micro controller. Finding a solution for the limited amount of memory of the ATTiny85 was fun, but having a little bit more memory would probably allow me to add a few nice effects.
  • If I did have an other micro controller, I’d probably have more GPIO pins, which allowed me to add more buttons, and a nice colorful LED. Every project is better with a LED.
  • In the future, I’ll definitely check EVERY footprint before I order my PCB’s.
  • Work on the software on the breadboard prototype before you finish up the PCB. This would have showed me the limiting factor of the ATTiny85. I would have given me a heads up before I pulled out all my hairs during the search for a solution.

If you have any question or suggestions about this project, feel free to leave them in the comments down below.

image
Loading comments …
©2021 - MichaelTeeuw.nl