No text on OLED 128x64 from ATTiny85

Are you able to run the I2C scanner sketch on the Tiny? If there’s no Serial available (or would share pins with I2C), maybe you can at least blink an LED at two different rates (for 'at least one I2C device found and none found) to get an output, then narrow it down to a “the specific address of the OLED display found”. It might be missing I2C pullups in hardware, there ight be an error in the library for the OLED or the I2C driver, it might crash during runtime, but checking the I2C availability is at least a starting point.

In trying to fix the OLED problem I created a new project I2C_Scanner. Nothing comes out on the Serial Monitor. When ‘Enter port index or full name:’ comes up I tried with ‘1’ which is the post in Windows.

Code:

#define I2C_TIMEOUT 0
#define I2C_NOINTERRUPT 0
#define I2C_FASTMODE 0
#define FAC 1
#define I2C_CPUFREQ (F_CPU/FAC)

/* Corresponds to A4/A5 - the hardware I2C pins on Arduinos */
/* Adjust to your own liking */
#define SDA_PORT PORTB
#define SDA_PIN 5
#define SCL_PORT PORTB
#define SCL_PIN 7
#define I2C_FASTMODE 0

#include <SoftI2CMaster.h>
#include <avr/io.h>


void CPUSlowDown(int fac) {
  // slow down processor by a fac
    CLKPR = _BV(CLKPCE);
    CLKPR = _BV(CLKPS1) | _BV(CLKPS0);
}
  

void setup(void) {
#if FAC != 1
  CPUSlowDown(FAC);
#endif

  Serial.begin(9600); // change baudrate to 2400 on terminal when low CPU freq!
  Serial.println(F("Intializing ..."));
  Serial.print("I2C delay counter: ");
  Serial.println(I2C_DELAY_COUNTER);
  if (!i2c_init()) 
    Serial.println(F("Initialization error. SDA or SCL are low"));
  else
    Serial.println(F("...done"));
}

void loop(void)
{
  uint8_t add = 0;
  int found = false;
  Serial.println("Scanning ...");

  Serial.println("       8-bit 7-bit addr");
  // try read
  do 
  {
    delay(100);
    if (i2c_start(add | I2C_READ)) 
    {
      found = true;
      i2c_read(true);
      i2c_stop();
      Serial.print("Read:   0x");
      if (add < 0x0F) Serial.print(0, HEX);
      Serial.print(add+I2C_READ, HEX);
      Serial.print("  0x");
      if (add>>1 < 0x0F) Serial.print(0, HEX);
      Serial.println(add>>1, HEX);
    } 
    else i2c_stop();
    add += 2;
  } while (add);

  // try write
  add = 0;
  do 
  {
    if (i2c_start(add | I2C_WRITE)) {
      found = true;
      i2c_stop();
      Serial.print("Write:  0x");    
      if (add < 0x0F) Serial.print(0, HEX);  
      Serial.print(add+I2C_WRITE, HEX);
      Serial.print("  0x");
      if (add>>1 < 0x0F) Serial.print(0, HEX);
      Serial.println(add>>1, HEX);
    } else i2c_stop();
    i2c_stop();
    add += 2;
  } while (add);
  if (!found) Serial.println(F("No I2C device found."));
  Serial.println("Done\n\n");
  delay(1000/FAC);
}

PlatformIO.ini

[env:attiny85]
platform = atmelavr
board = attiny85
framework = arduino
lib_deps = felias-fogg/SoftI2CMaster@^2.1.3
upload_port = COM1

Serial Monitor

> Executing task in folder I2C_Scanner: C:\Users\hans\.platformio\penv\Scripts\platformio.exe device monitor <

--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at http://bit.ly/pio-monitor-filters

--- Available ports:
---  1: COM1                 'Communications Port (COM1)'
--- Enter port index or full name: 1
--- Miniterm on COM1  9600,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---

Do you have a dedicated USB-UART adapter hooked up to the ATTiny where the UART signal is output? This doesn’t go over the programmer (works via SPI).

I’ve looked into the code a bit further and see that Serial outputs on the pin with the “AIN0” function (PB0) and RX is on “AIN1” (PB1) (source, source). Sadly that overlaps Hardware I2C pins butsince your sketch above uses a software I2C with definiable pins that should be no problem.

So you would need to have such an adapter and connect the RX of the adapter to PB0 of the ATTiny and GND to GND between them.

So, I’ve got an adapter and connected D- (Rx) to PB0 and Gnd to Gnd. The OLEDs SDA is also connected to PB0. It compiles and uploads with no errors but there is nothing on the Serial monitor. The adapter is connected to a second USB on my PC and I’ve added an line in the platform.ini to that effect (monitor_port = COM4).

Does it say “D-” on the adapter? That’s the signal name for USB Data minus (D+, D-, VUSB, GND are the USB lines). Do you have a link to the adapter?

When you just do a simple sketch like

#include <Arduino.h>

void setup() { Serial.begin(9600); }
void loop() { Serial.println("Test"); delay(500); }

do you see any output on PB0? Can you probe other pins of the ATTiny to find the right one (even though it should really be the right one)?

The adapter has four wires, red, black, white and green. I looked up the USB standard and the white is D- (RX) and the green is D+ (TX) so I connected D- to PB0 and the GND to GND. Port COM4 where the adapter is cannot be opened. I moved D- to PB1 - PB4 in turn to see if there was any difference and there was none. Changed monitor_port to COM[1234] but no change but there was a different comment in the ouput:
— More details at Redirecting...
— Miniterm on COM1 9600,8,N,1 —
Don’t know if this has anything to do with my change.

    > Executing task in folder ATtiny85_test: C:\Users\hans\.platformio\penv\Scripts\platformio.exe device monitor <

--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at http://bit.ly/pio-monitor-filters
could not open port 'COM4': could not open port 'COM4': OSError(22, 'A device which does not exist was specified.', None, 433)
The terminal process "C:\Users\hans\.platformio\penv\Scripts\platformio.exe 'device', 'monitor'" terminated with exit code: 1.

Terminal will be reused by tasks, press any key to close it.

Nono, the side that is supposed to go to the microcontroller is the UART side of the adapter, and not the USB one. UART has no D- and D+ signal. The appropriate adapters for that usually have a FTDI (FT232RL) or CH340 chip on. (example, example).

Also here is a doc and a tutorial.

What does your adapter look like?

The adapter lokks like this
kabel
I have ordered a proper Sparkfun adapter with genuine chips and micro-USB but in the meantime I searched and found a new driver and in Device Manager it says “Prolific USB-to-Serial Comm Port (COM6)” but it still doesn’t work. The new adapter will arrive soon.

Hm with

from here It looks right / usable though. You should be able to connect GND from the adapter to GND of the ATTiny and the white (RXD) cable to PB0 of the ATTiny. You tried this exact configuration?

Yes, I tried now moving the the white (RX) from PB0 thru PB5 and compiled and uploaded inbetween and it says it can find COM6. My adapter is a Prolific PL2303 HX.

To my big surprise I found another adapter, lying forgotten in my bag of things. It is a Silicon Labs CP210x. I connected it and now I get the monitor output “Test” twice a second. So, what can we say about the other adapter, throw it away?

Mhmm I don’t know – maybe the wireup was wrong after all.

Does the I2C scanner sketch output something now that you can see the Serial output?

ATtiny85_bb

I changed the COM baud rate to 9600 because that was what it showed in Device Manager for that port. I also measured some voltages on the OLED: Vcc=5, SDA=4.8, SCL=4.8. I don’t know if that’s normal or not. There’s a permission error at the end of the compiler output.
> Executing task in folder I2C_Scanner: C:\Users\hans.platformio\penv\Scripts\platformio.exe device monitor <

--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at http://bit.ly/pio-monitor-filters
--- Miniterm on COM3  9600,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
␀Exception in thread rx:
Traceback (most recent call last):
  File "C:\Users\hans\.platformio\python3\lib\threading.py", line 926, in _bootstrap_inner
    self.run()
  File "C:\Users\hans\.platformio\python3\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "c:\users\hans\.platformio\penv\lib\site-packages\serial\tools\miniterm.py", line 499, in reader
    data = self.serial.read(self.serial.in_waiting or 1)
  File "c:\users\hans\.platformio\penv\lib\site-packages\serial\serialwin32.py", line 259, in in_waiting
    raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
serial.serialutil.SerialException: ClearCommError failed (PermissionError(13, 'Access is denied.', None, 5))    indent preformatted text by 4 spaces

Try to change pins for I2C if necessary to resolve the conflict.

Correct me if I’m wrong but looking at my pinout
SDA is pin 5 (PB0)
SCL is pin 7 (PB2)
and there’s no PB7.
BTW, what does ‘#define SDA_PORT PORTB’ mean?

Indeed. Seems to have been written for some ATTiny with higher pin counter.

Anyhow, I’ve played around with my ATTiny85 contained on the Digispark / DigiStump and I have a working serial monitor and can see my SSD1306 I2C OLED display on the bus using the scanner above.

I did however have to add 4.7kOhm pullup resistors on my SDA and SCL lines – without these pullups I2C doesn’t work, and my board doesn’t have them.

I’m using a different Arduino core implementation here (for the digistump) but since it’s a ATTiny85 it should work regardless.

You can try the platformio.ini

[env:digispark-tiny]
platform = atmelavr
board = digispark-tiny
framework = arduino
lib_deps = felias-fogg/SoftI2CMaster@^2.1.3

(but keep your upload_protocol to upload via your usbtiny)

with code

#define I2C_TIMEOUT 0
#define I2C_NOINTERRUPT 0
#define I2C_FASTMODE 0
#define FAC 1
#define I2C_CPUFREQ (F_CPU/FAC)

/* Corresponds to A4/A5 - the hardware I2C pins on Arduinos */
/* Adjust to your own liking */
#define SDA_PORT PORTB
#define SDA_PIN 0
#define SCL_PORT PORTB
#define SCL_PIN 1
#define I2C_FASTMODE 0

#include <SoftI2CMaster.h>
#include <avr/io.h>


void CPUSlowDown(int fac) {
  // slow down processor by a fac
    CLKPR = _BV(CLKPCE);
    CLKPR = _BV(CLKPS1) | _BV(CLKPS0);
}
  

void setup(void) {
#if FAC != 1
  CPUSlowDown(FAC);
#endif

  Serial.begin(9600); // change baudrate to 2400 on terminal when low CPU freq!
  Serial.println(F("Intializing ..."));
  Serial.print("I2C delay counter: ");
  Serial.println(I2C_DELAY_COUNTER);
  if (!i2c_init()) 
    Serial.println(F("Initialization error. SDA or SCL are low"));
  else
    Serial.println(F("...done"));
}

void loop(void)
{
  uint8_t add = 0;
  int found = false;
  Serial.println("Scanning ...");

  Serial.println("       8-bit 7-bit addr");
  // try read
  do 
  {
    delay(100);
    if (i2c_start(add | I2C_READ)) 
    {
      found = true;
      i2c_read(true);
      i2c_stop();
      Serial.print("Read:   0x");
      if (add < 0x0F) Serial.print(0, HEX);
      Serial.print(add+I2C_READ, HEX);
      Serial.print("  0x");
      if (add>>1 < 0x0F) Serial.print(0, HEX);
      Serial.println(add>>1, HEX);
    } 
    else i2c_stop();
    add += 2;
  } while (add);

  // try write
  add = 0;
  do 
  {
    if (i2c_start(add | I2C_WRITE)) {
      found = true;
      i2c_stop();
      Serial.print("Write:  0x");    
      if (add < 0x0F) Serial.print(0, HEX);  
      Serial.print(add+I2C_WRITE, HEX);
      Serial.print("  0x");
      if (add>>1 < 0x0F) Serial.print(0, HEX);
      Serial.println(add>>1, HEX);
    } else i2c_stop();
    i2c_stop();
    add += 2;
  } while (add);
  if (!found) Serial.println(F("No I2C device found."));
  Serial.println("Done\n\n");
  delay(1000/FAC);
}

You’ll see that I’ve also adapted SDA to be on PB0 and SCL to be on PB1 since that’s nicely software-definable.

Hardware wireup: From this

  • Connected 5V to VCC of OLED
  • Connected GND to GND of OLED
  • Connected PB0 (SDA) to a line on a breadboard, on which a pull-up resistor if 4.7kOhm towards 5V is placed, then onwards to the SDA of the OLED
  • Connected PB1 (SCL) to a line on a breadboard, on which a pull-up resistor if 4.7kOhm towards 5V is placed, then onwards to the SCL of the OLED
  • Connected PB2 of Attiny to USB-Serial converter’s RX

If you get to the point where you can see on the serial monitor output that a device on the I2C bus is recognized, that’s a huge step forward.

For me it outputs

Scanning ...
       8-bit 7-bit addr
Read:   0x79  0x3C
Write:  0x78  0x3C
Done

which is perfect since my SSD1306 is on that address, 0x3C as a 7-bit address.

It creates a macro for SDA_PORT that maps to the value PORTB which is in turn a macro mapping to the (start) address of the PORTB registers. PORTB is the “GPIO Port B”, with it’s members PB0, PB1, . etc. PB0 means nothing but Port B, Pin 0. Most devices havee more GPIO ports, like PORTA, PORTC, PORTD etc. See e.g. this reference.

Yeah after that it’s easy. A slight adaption of ss_oled/ss_oled_test.ino at master · bitbank2/ss_oled · GitHub (early cutoff of loop so that not so many functions are executed, otherwise not enough FLASH) makes my OLED display something :slight_smile:

The Digispark is in the USB hub there at the red light.

My platformio.ini

[env:digispark-tiny]
platform = atmelavr
board = digispark-tiny
framework = arduino
lib_deps =
    https://github.com/bitbank2/ss_oled.git

code

#include <Arduino.h>
#include <ss_oled.h>

// Arduino Pro Mini
// Pin 8 (0xb0 = PORTB, bit 0)
// Pin 9 (0xb1 = PORTB, bit 1)
#define SDA_PIN 0xb0
#define SCL_PIN 0xb1
#define RESET_PIN -1
int rc;
SSOLED oled;
void setup() {
  uint8_t uc[8];
    
  rc = oledInit(&oled, OLED_128x64, 0x3c, 0, 0, 0, SDA_PIN, SCL_PIN, RESET_PIN, 1000000L);
  if (rc != OLED_NOT_FOUND)
  { 
    oledFill(&oled, 0,1);
    oledSetContrast(&oled, 127);
    oledWriteString(&oled, 0,0,0,(char *)"**************** ", FONT_8x8, 0, 1);
    oledWriteString(&oled, 0,4,1,(char *)"BitBank SS_OLED", FONT_8x8, 0, 1);
    oledWriteString(&oled, 0,8,2,(char *)"running on the", FONT_8x8, 0, 1);
    oledWriteString(&oled, 0,8,3,(char *)"SSD1306 128x64", FONT_8x8, 0, 1);
    oledWriteString(&oled, 0,4,4,(char *)"monochrome OLED", FONT_8x8, 0, 1);
    oledWriteString(&oled, 0,24,5,(char *)"Written By", FONT_8x8, 0, 1);
    oledWriteString(&oled, 0,24,6,(char *)"Larry Bank", FONT_8x8, 0, 1);
    oledWriteString(&oled, 0,0,7,(char *)"**************** ", FONT_8x8, 0, 1);
    delay(4000);
  }
}

void loop() {
  
int i, j;
char szTemp[32];

  oledFill(&oled, 0,1);
  oledWriteString(&oled, 0,0,0,(char *)"Now with 5 font sizes", FONT_6x8, 0, 1);
  oledWriteString(&oled, 0,0,1,(char *)"6x8 8x8 16x16", FONT_8x8, 0, 1);
  oledWriteString(&oled, 0,0,2,(char *)"16x32 and a new", FONT_8x8, 0, 1);
  oledWriteString(&oled, 0,0,3,(char *)"Stretched", FONT_12x16, 0, 1);
  oledWriteString(&oled, 0,0,5,(char *)"from 6x8", FONT_12x16, 0, 1);
  delay(10000);
  return;
  
  oledFill(&oled, 0, 1);
  oledSetTextWrap(&oled, 1);
  oledWriteString(&oled, 0,-1,-1,"This is a test of text wrap", FONT_6x8, 0, 1);
  delay(3000);
  oledFill(&oled, 0,1);
//  oledSetTextWrap(0);
  oledWriteString(&oled, 0,-1,-1,"This ", FONT_16x16, 0, 1);
  oledWriteString(&oled, 0,-1,-1,"is a ", FONT_16x16, 0, 1);
  oledWriteString(&oled, 0,-1,-1,"test of text wrap", FONT_16x16, 0, 1);
  delay(3000);
  oledFill(&oled, 0,1);
  oledSetCursor(&oled, 40,4);
  oledWriteString(&oled, 0,-1,-1,"Middle", FONT_6x8,0,1);
  delay(3000);
  if (rc >= OLED_SH1106_3C)          // We can set pixels on the SH1106 without a back buffer
  {
    int x, y;
    for (i=0; i<2500; i++)
    {
      x = random(128);
      y = random(64);
       oledSetPixel(&oled, x, y, 1, 1);
    }
    delay(2000);
  }
#ifndef __AVR__

  for (i=0; i<8; i++)
  {
    sprintf(szTemp, "Line %d", i);
    oledWriteString(&oled, 0,0,i,szTemp, FONT_8x8, 0, 0);
    oledWriteString(&oled, 0,64,i,szTemp, FONT_8x8, 0, 0);
  } // for i
  j = 0; // missing line
  while (1)
  {
    for (i=0; i<8; i++) // smooth scroll 8 lines
    {
//void oledScrollBuffer(int iStartCol, int iEndCol, int iStartRow, int iEndRow, int bUp);
      oledScrollBuffer(&oled, 0,63,0,7,1);
      oledScrollBuffer(&oled, 64,127,0,7,0);
      oledDumpBuffer(&oled, NULL);
//void oledDrawGFX(uint8_t *pSrc, int iSrcCol, int iSrcRow, int iDestCol, int iDestRow, int iWidth, int iHeight, int iSrcPitch);
//      oledDrawGFX(NULL, 0, 0, 0, 0, 64, 7, 0); // left half
//      oledDrawGFX(NULL, 64, 1, 64, 0, 64, 7, 0); // right half 
      delay(40);
    }
    // fill in the missing line which scrolls off
    sprintf(szTemp, "Line %d", j & 7);
    oledWriteString(&oled, 0,0,7,szTemp, FONT_NORMAL, 0, 0);
    sprintf(szTemp, "Line %d", 7-(j & 7));
    oledWriteString(&oled, 0,64,0,szTemp, FONT_NORMAL, 0, 0);
    j++;
  }
#else
  for (i=0; i<256; i++)
  {
    oledWriteString(&oled, i, 0,0,(char *)"This is a scrolling text demo showing how a long string can be displayed ", FONT_NORMAL, 0, 1);
  }
#endif // __AVR__
} // loop

(Note: It did need one powercycle after flashing for me to work.)

I haven’t done any testing today - been away all day but I was thinking. You said I need two pullup resistors. Aren’t there internal pullups in ATTiny that you can set with 'pinMode(PB1, INPUT/OUTPUT) ?

There may be weak pullup resistors in the ATTiny (in the range of… 100K)? I’m not sure if those are strong enough for I2C. Usually they’re 4.7kOhm for 5V.

You can try with pinMode(PB1, INPUT_PULLUP); and the other pin too.

The internal pullups are not enough I’ve read so I wired in two 4.7kΩ resistors. There’s no life in the setup at all. It’s really frustrating. Is it easy to debug the ATTiny85?