Squeezing the last few bytes out of my code - Digispark clone

Hi guys

I’m back again with another challenge;

The hardware:
I’m playing with a digispark clone - and an OLED display.
That works fine
P0 - SDA
P1 - N/C
P2 - SCK (typo on board, should be SCL)
P3 - N/C
P4 - N/C
P5 - N/C (used as reset or something in clones - this one is off limits anyway)

The code:
I’m trying to make a kitchen timer-like project that counts seconds since a switch was flicked.

The problem:
I need to free 60 bytes to make it fit.

What’s been tried:
Replaced the bootloaded for a space-aggressive version (micronucleus 1.11)
Used boolean instead of integer
Cleared out all code notation (yes I know this doesn’t make a difference, made me feel better though)
Attempted to reference gpio directly, instead of using digitalread (need some help with this)
Tried to streamline Tiny4kOLED.h to remove unused fonts (I only need one)

My code (modified from example):

#include <Tiny4kOLED.h>
#include <TinyWireM.h>

int Sec = 0;
boolean BS = false;

void setup() {
  oled.begin();
  oled.clear();
  oled.on();
  pinMode(1, INPUT);
  
}

void loop() {
delay(1000);
BS = digitalRead(1);
if (BS == true); { 
    oled.clear();
    oled.setFont(FONT16X32);
    oled.setCursor(0, 0);
    oled.print(Sec);
    Sec = Sec + 1;
  }
}

Greatly appreciate your time guys - any suggestions will help me build my skill with C++

Instead of digitalRead() you can read the PINx register for whichever Port pin 1 is on your chip (an ATtiny, right? Maybe only has PortA) and mask all but the bit for that pin. It's a 2 cycle operation that does none of the fool-proofing that digitalRead does.

You can set the Port up by writing to the DDRx and PORTx registers.

From the main Arduino site:
https://www.arduino.cc/en/Reference/PortManipulation

Also set the font in setup since it never changes.

Yes it's an Attiny85 or something similar that the chinese are using.

this would be the reference I've been looking for.

I've done very little direct addressing like this, least of all in C++ (did a little in basic and also vb6 in the past) so I'll have a read and a play and see what I can manage.

You really want the full chip datasheet, all the registers are laid out and explained.

With AVR you read pins on the PINx register. If you write any bits to the same register, the matching PORTx bits will flip letting you toggle pins in 1 cycle.

This site is a gold mine for Arduino know-how. It's not mine, it's just really good.
http://gammon.com.au/forum/index.php?bbtopic_id=123

Tried to streamline Tiny4kOLED.h to remove unused fonts (I only need one)

have you hacked the font to only include the 0..9 digits, space ' ' and may be the ':' (and other few letters) if you need them -> that would free up space too

You would replace the characters just after the numbers with the drawing of your characters for example instead of ';' in #17 you could replace it with a space and hack a bit the library to provide an offset so that you can get rid of the first symbols

(the unused font would not be compiled into your program, the optimizer should throw them away if you never reference the font array)

May be showing my ignorance of the attiny85, but is it possible to just not have the bootloader and instead use that space for the sketch?

J-M-L:
have you hacked the font to only include the 0…9 digits, space ’ ’ and may be the ‘:’ (and other few letters) if you need them → that would free up space too

You would replace the characters just after the numbers with the drawing of your characters for example instead of ‘;’ in #17 you could replace it with a space and hack a bit the library to provide an offset so that you can get rid of the first symbols

(the unused font would not be compiled into your program, the optimizer should throw them away if you never reference the font array)

Not yet - but that offers me a convenient avenue to explore - Will follow this one up
Pretty sure I can gain a few bytes back that way.

GoForSmoke:
You really want the full chip datasheet, all the registers are laid out and explained.

With AVR you read pins on the PINx register. If you write any bits to the same register, the matching PORTx bits will flip letting you toggle pins in 1 cycle.

This site is a gold mine for Arduino know-how. It’s not mine, it’s just really good.
Gammon Forum : Electronics : Microprocessors

Speed really isn’t my issue - space is, but I can see this would do both.
Direct referencing is uncharted territory for me, and I’ll probably be on that learning curb for a few days
But this is the direction I had turned to - and have printed your comment along with a couple of others so I can scribble on it as I research this method.

david_2018:
May be showing my ignorance of the attiny85, but is it possible to just not have the bootloader and instead use that space for the sketch?

Yes that was another thought - but with current tools available to me, that’s not as easy.
I’m hoping to mass produce a product - and USB programming is about 100X more convenient for me, for various reasons not listed here.

Slumpert:
Not sure why you don’t just delete the functions your not using from the tiny4k library.

Your scope is basic, so there are numerous functions that your not using than should be easily deleted. Such as…

Agreed - this is also a direction I have been heading - but experience and understanding of what/where to remove functions is weak in my case.

However this was part of the purpose behind attempting this project in the first place - and I’ll also print this for follow-up… in sequential order of posting lol.

"The problem:
I need to free 60 bytes to make it fit."

I was thinking RAM. Did I guess right?

Have you already used the EEPROM, 512 bytes?

"Not sure why you don't just delete the functions your not using from the tiny4k library."

The compiler already does that. Try adding Serial prints to BLINK and see what happens to the size of the hex file.

and reading posts made while typing/looking things up I see a biig one, I thought that no one bootloads ATtinys!

A quick update:

Removing some of the unusued characters in the 16X32 font dropped my code size back to 5904 bytes

I can at least test using the digitalread function.

after that, I'll continue to work on the other suggestions, and see just how lean I can make this run.

In the case of bootloader - the digispark has a built-in usb connection on the board. It's quite convenient.

A further update -

My Oled is doing something funny now.

It skips number, characters appear as "white noise" randomly,
and it won't add an extra digit.

I went back to my original test code - known working.
same result.

I restored the original font file
same result

I replaced the OLED for a fresh one
same result

I replaced the digispark
Same result

I rewrote the code from scratch
same result.

tested oled on ATmega2650 with example code
works fine

tested on arduino uno and nano
both drive oled fine.

I've had setbacks like this before, but usually I can find the issue.
I think I need to walk away from this for a day or two till I work out what happened.

Update as of next morning - discovered that I have to restart the Arduino IDE after each edit of the .h font files.

it looks like my method of editing the files is causing the character corruption.

More learning ahead.

I’ve changed my code to a more simple timer, for the sake of experimenting with space issues.
when I have worked out that issue, I’ll move onto to directly accessing the pins.

current code:

#include <Tiny4kOLED.h>
#include <TinyWireM.h>

int H = 0;
int M = 0;
int S = 0;

void setup() {
  oled.begin();
  oled.clear();
  oled.on();
}

void loop() {
delay(1000);
oled.clear();
oled.setFont(FONT6X8);
oled.setCursor(0, 1);
oled.print("Time to Apocalypse:");
oled.setCursor(0, 2);
oled.println(H);
oled.setCursor(16, 2);
oled.println(M);
oled.setCursor(32, 2);
oled.println(S);
S = S + 1;
if (S == 60){
  S = 0;
  M = M + 1;
}
if (M = 60){
  M = 0;
  H = H + 1;
}
}

What’s your actual memory issue: SRAM or FLASH?

Flash space

The issue is that IDO won't let me write the program because it consumes "101% of available space."

so trimming the end of your font should really help

Seems to save a bit of program memory if you change H, M, and S to unsigned integer or byte. Presumably those are hours, minutes, and seconds so none should exceed 60 or be negative.

This is likely the link to the font you use when you dooled.setFont(FONT6X8);

What I suggest is you trim the end of the font after the digits

const uint8_t ssd1306xled_font6x8 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //   0 
  0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // ! 1 
  0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // " 2 
  0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // # 3 
  0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $ 4 
  0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // % 5 
  0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // & 6 
  0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // ' 7 
  0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // ( 8 
  0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // ) 9 
  0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // * 10
  0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // + 11
  0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // , 12
  0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // - 13
  0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // . 14
  0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // / 15
  0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 16
  0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 17
  0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2 18
  0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3 19
  0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 20
  0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5 21
  0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 22
  0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7 23
  0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8 24
  0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 25
  0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // : 26
};

as it’s in PROGMEM that will free up flash memory for 68 characters each using 6 bytes so you should reclaim 408 bytes.

Of course you need to ensure you only try to display the characters that are now defined.

If you need some letters you could replace an existing glyph by the drawing of another, for example if you need the letter ‘H’ which is defined with0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H 40and don’t need the ‘%’ symbol which is entry number 5 in the font, just replace 0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // % 5 with 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H 5 and every time you want to display an ‘H’ on screen just put ‘%’ instead.

As you don’t change where digits are the library should keep working fine. But again don’t call anything further than the ‘:’ entry in the array or you’ll display garbage (try to read in flash where there is nothing defining a symbol)

Are you using the modified Tiny4kOLED library from the "Large fonts on a tiny OLED & ATiny85" webpage? ? ? ? I see your original example used 16x32 font. If you are using that library, it appears to load all the fonts into program memory, regardless of which one you are using. Doesn't seem to work if you simply delete the font6x8.h and font8x16.h files, but will compile if you reduce those font arrays to a single byte of 0x00, with very substantial memory savings. Not a lot to remove in the 16x32 font, it appears to be intended for clock use and doesn't have a lot of excess characters.

EDIT:
Looking at the Tiny4kOLED.h file, near the beginning are the lines:

/* Uncomment from the 3 line below this, the fonts you intend to use:
*  FONT6X8  = 764 bytes
*  FONT8X16 = 1776 bytes
*  FONT16X32= 1334 bytes
*/

#define FONT6X8	 0
#define FONT8X16	 1
#define FONT16X32 	2

comment out the lines for the fonts you do not need, and you don't need to modify any of the font files.

Indeed it does, but something I have done causes the font to display like white noise on a CRT TV.

I suspect my efforts to trim the extra characters have not been correct.
I'm using notepad to do the editing, I suspect I need something like notepad++ that will respect the code indentation.

at this stage I'm not entirely sure what's happening, and I'll try to find some time to make a video that details my issues.

Dranoweb:
In the case of bootloader - the digispark has a built-in usb connection on the board. It's quite convenient.

I suggest just get rid of the bootloader. You don't need it. It may be convenient, but it is also very easy to upload using ICSP. If you're using the bootloader because you like to disable reset to get the extra pin, it is also somewhat easy to reset the fuses with high voltage programming, then upload your sketch via ICSP and set the fuses again to disable reset.
If you are doing this for the education or exercise that is fine, but if you just want to get it done, DF Robot Beetle is the same size as Digispark, but it is a Leonardo with no more space shortage and effortless upload and real USB serial for debugging.