Feather M0 + 128x32 mono OLED

WARNING: I experienced problems with the OLED not being responsive to a Feather M0 reset if the serial port was in use.   If you use the serial monitor, all works okay.  


/*

Adfruit Feather M0 + 128x32 mono OLED FeatherWing

Adafruit Feather M0 Basic Proto - ATSAMD21 Cortex M0
The ATSAMD21G18 ARM Cortex M0 processor is the same as the new Arduino Zero.
https://www.adafruit.com/product/2772
https://learn.adafruit.com/adafruit-feather-m0-basic-proto?view=all
https://cdn-learn.adafruit.com/assets/assets/000/046/244/original/adafruit_products_Feather_M0_Basic_Proto_v2.2-1.png?1504885373
https://learn.adafruit.com/adafruit-feather-m0-basic-proto/adapting-sketches-to-m0

Pins:

GND   ground
USB   You can supply power to the M0 directly to this pin when the USB connector is not used.
EN   connect to ground to disable the 3.3V regulator
3V   output from 3.3V regulator.   500 mA peak.
#0   Serial1 hardware UART Rx
#1   Serial1 harsware UART Tx
#5   GPIO #5 (OLED FeatherWing button)
#6   GPIO #6 (OLED FeatherWing button)
#9   GPIO #9 & A7 for LiPi voltage divider. (OLED FeatherWing button)
#10,#11,#12    GPIO #10, #11, #12
#13   red LED next to the USB jack
A0   Analog input & true output.   0 to 3.3V
#A1 to A5   analog input (12-bit) or digital I/O
ARef   analog reference pin @ 3.3V
#20   I2C SDA.   Use 2.2K-10K pullup
#21   I2C SCL.   Use 2.2K-10K pullup
#24, #23, #22   SPI SCK/MOSI/MISO
RST   connect to ground to reset AVR and launch bootloader manually




Adafruit 128x32 mono OLED FeatherWing
https://www.adafruit.com/product/2900

28x32 monochrome OLED + three user buttons.
Display area: ~25.8mm / ~1.0"

The OLED I2C address is 0x3C and cannot be changed

https://learn.adafruit.com/adafruit-oled-featherwing?view=all
https://learn.adafruit.com/adafruit-gfx-graphics-library?view=all
http://henrysbench.capnfatz.com/henrys-bench/arduino-adafruit-gfx-library-user-guide/
http://engineeringnotes.blogspot.com/2013/07/using-ssd1306-textgraphics-display.html

This sketch demonstrates writing text to the OLED
using the Adafruit GFX Graphics Library.  It is derived
from the Adafruit example at: https://learn.adafruit.com/adafruit-oled-featherwing?view=all

*** WARNING ***
    Using Serial causes the OLED to not be reset when the M0 is initialized.  
    If you use the serial monitor, the OLED will work properly.
    Do not plan on using serial with the OLED in a deployed configuration.  

*/

//////////////////////////////////////////////////////////////////////////////
#include 
#include 
#include 
#include 

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// NOTE: The FeatherWing OLED has an auto-reset chip, so you don't need to declare a reset pin.
// set OLED_RESET to -1 or dont pass it in, the reset button is hard-coded to the OLED reset
Adafruit_SSD1306 display(128, 32, &Wire);
//////////////////////////////////////////////////////////////////////////////
// M0  (Feather M0)
const byte pin_LED = 13;
const byte pin_BATT = A7;
const byte pin_Button_A = 9;
const byte pin_Button_B = 6;
const byte pin_Button_C = 5;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//  ADC constants
// Constants below used to conver raw ADC values to mV.
// 10 bit, 0 to 1023, or 1024 steps
// 12 bit, 0 to 4095, or 4096 steps
const float FSadc = 1024.0;
const float FSmV = 3300.0;
//////////////////////////////////////////////////////////////////////////////
//  lipoly battery
//  The pin is defined earlier as pin_BAT
// dAdj_mV is multiplied by the reading at pinADC in mV to get the input voltage to a
// voltage divider or other component. 
// Shunt cal voltage divider; R1 = 10k ohms, R2 = 2.2 k ohms; 
const float dAdj_mVbat = 2.0;
// 300000 ms = 5 min
// 60000 ms = 1 min
// 10000 ms = 10 sec = 0.1 Hz 
// 1000 ms = 1 sec = 1 Hz
// 100 ms = 0.1 sec = 10 Hz
// 10 ms = 0.01 sec = 100 Hz
const unsigned long batInterval = 50000;  
unsigned long lastBat = 0;  // timer
//////////////////////////////////////////////////////////////////////////////
//  Analog input
const byte pinADC = A0;
// dAdj_mV is multiplied by the reading at pinADC in mV to get the input voltage to a
// voltage divider or other component. 
// Shunt cal voltage divider; R1 = 10k ohms, R2 = 2.2 k ohms; 
const float dAdj_mV = (10000.0+2200.0)/2200.0;
// 3.3 V full scale (3300 mV); 0 to 1023 steps (1024 total steps) for 10 bit
// 187 ADC reading * (3300/1024) = 600 mV
int iSamples = 0;
// It takes ~ 1 sec to collect 1000 samples. 
const int iMaxSamples = 1000;
float dCumAvg = 0.0;
float dLastAvg = 0.0;
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// Show serial messages when DEBUG = true, otherwise minimize them.
// WARNING:   I experienced the Feather M0 waiting for the serial monitor to be
//            loaded from the IDE before the sketch would continue to run.  
//            For this reason, I am wrapping serial output around a DEBUG check.
//
//    Using Serial causes the OLED to not be reset when the M0 is initialized.  
//    If you use the serial monitor, the OLED will work properly.
//    Do not plan on using serial with the OLED in a deployed configuration.  
#define DEBUG false
//////////////////////////////////////////////////////////////////////////////


void setup() {
  #if DEBUG
    Serial.begin(9600);
    while (!Serial) {
      delay(1);
    }
    Serial.println("Setup complete");
  #endif

  //Serial.println("Adafruit 128x32 mono OLED FeatherWing text command demo");
  
  // by default, generate the high voltage from the 3.3v line internally.
  // initialize with the I2C addr 0x3C (for the 128x32)
  delay(100);  // Give the OLED driver time to bring it's internal charge up to voltage.
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32
    blinkLED(pin_LED);
    for(;;); // Don't proceed, loop forever
  }
  
  // Show image buffer on the display hardware.
  // Since the buffer is intialized with an Adafruit splashscreen
  // internally, this will display the splashscreen.
  // The image buffer on the display hardware is initialized
  // with an Adafruit splashscreen.  If you execute the command
  // display.display(), then the splashscreen will be shown.
  // Disable this command if you don't want to display the 
  // splashscreen. 
  //display.display();
  //delay(1000);

  // Clear the display buffer.
  display.clearDisplay();
  display.display();

  // Configure the three buttons on the OLED FeatherWing
  // Button A is DIO9 (note this is also used for the battery voltage divider.  
  // *** WARNING:  Must disable the pullup when you analog read to check the battery voltage !!!
  pinMode(pin_Button_A, INPUT_PULLUP);  // On BUTTON_A and used by battery monitor on pin_BATT
  delay(10);
  //pinMode(pin_Button_B, INPUT_PULLUP); // Button B has a 100k pullup on it from the OLED so it will work with the ESP8266.  Therefore do NOT set the internal pullup. 
  delay(10);
  pinMode(pin_Button_C, INPUT_PULLUP);
  delay(10);

  // text display tests
  //Note that for .setTextSize(1), a character is 6 pixels wide by 8 pixels high.
  //For a 128x32 OLED, 128/6 = 12 characters; 32/8 = 4 characters
  display.setTextSize(1);
  
  //display.setTextColor();  BLACK,BLUE,RED,GREEN,CYAN,MAGENTA,YELLOW,WHITE
  //display.setTextColor(text color, background color);
  //Sets the text color and background color the text will print on.
  //For monochrome (single-color) displays, colors are always specified as simply 1 (set) or 0 (clear).
  display.setTextColor(WHITE);
  
  // Turn off text wrapping
  display.setTextWrap(false);
  
  // display.setFont(const GFX font);
  // See font information at: https://learn.adafruit.com/adafruit-gfx-graphics-library?view=all
  
  //display.setCursor(horizontal position, vertical position);
  //For .setTextSize(1), rows are: 0, 8, 16, 24
  //                     21 characters horizontally for .setTextSize(1)
  display.setCursor(0,0);
  display.print("AF 128x32 mono OLED");
  display.setCursor(0,15);
  display.print("SSD1306 display");
  // Call display.display() to make the prior drawing commands 
  // display on the OLED.  It is slow, so use sparingly. 
  display.display(); 
  delay(3000);

  // invert the display with this command
  display.invertDisplay(true);
  delay(3000);
  display.invertDisplay(false);
  delay(3000);

  // Note that it is not possible to programmatically reduce the brightness of the OLED with the Adafruit library.  
  // Another sw option may be:  https://code.google.com/archive/p/u8glib/downloads
  //  u8g.setContrast(n);  where n = 0..255
  // Hardware solution:  https://forums.adafruit.com/viewtopic.php?f=47&t=38569#p573606
  
  //  .setTextSize(1) demo
  display.clearDisplay();
  display.setCursor(0,0);
  display.print("012345678901234567890");
  //
  display.setCursor(0,8);
  display.print("setTextSize(1)");
  //
  display.setCursor(0,16);
  display.print("  21 characters");
  //
  display.setCursor(0,24);
  display.print("x  4 rows");
  display.display();
  delay(3000);

  //  .setTextSize(2) demo
  //  
  //  .setTextSize(2) will change the character sizes as follows:
  //    2*6 x 2*8 = 12x16 pixels.
  //  For a 128x32 OLED, 128/12 = 10 chars wide; 32/16 = 2 characters tall
  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(0,0);
  display.print("0123456789");
  //
  display.setCursor(0,16);
  display.print("10x2 chars");
  display.display();
  delay(3000);
  
  //  Mixed text sizes demo - large over small
  //  .setTextSize(2) & .setTextSize(1)
  display.clearDisplay();
  //
  display.setTextSize(2);
  display.setCursor(0,0);
  //display.print("0123456789");
  display.print("10x2 chars");
  //
  display.setCursor(0,16);
  display.setTextSize(1);
  display.print(".setTextSize(2)");
  //
  display.setCursor(0,24);
  display.setTextSize(1);
  display.print("10 chars x 2 rows");
  display.display();
  delay(3000);
  
  //  Mixed text sizes demo - small over large
  //  .setTextSize(1) & .setTextSize(2)
  display.clearDisplay();
  //
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("012345678901234567890");
  //
  display.setCursor(0,8);
  display.setTextSize(1);
  display.print(".setTextSize(1)");
  //
  display.setCursor(0,16);
  display.setTextSize(2);
  //display.print("0123456789");
  display.print("10x2 chars");
  display.display();
  delay(3000);
    
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("Press btn A, B, or C (batt)");
  display.display();

  #if DEBUG
    Serial.println("Setup complete");
  #endif
} // setup()


void loop() {
  if (digitalRead(pin_Button_A) != HIGH) {
    display.clearDisplay();
    display.display();
    yield();
    display.setTextSize(1);
    display.setCursor(0,0);
    display.print("Button A");
    #if DEBUG
      Serial.println("Button A");
    #endif
  }
  if (digitalRead(pin_Button_B) != HIGH) {
    display.clearDisplay();
    display.display();
    yield();
    display.setTextSize(1);
    display.setCursor(0,8);
    display.print("Button B");  }
    #if DEBUG
      Serial.println("Button B");
    #endif
  if (digitalRead(pin_Button_C) != HIGH) {
    display.clearDisplay();
    display.display();
    yield();
    display.setTextSize(1);
    display.setCursor(0,16);
    display.print("Button C");
    #if DEBUG
      Serial.println("Button C");
    #endif
    printLiPoBattV ();
  } 
  // The yield() function allows ESP8266 microcontroller to run a 
  // number of utility functions in the background, without causing 
  // the ESP8266 to crash or reset. Include it within any 
  // while() + digitalRead() and other loops;
  yield();
  display.display();
} // loop()


void printLiPoBattV () {
  // Note that #9 for BUTTON_A is also used for the battery
  // voltage divider A7, so if you want to use them both, make 
  // sure to disable the pullup when you analog read, then 
  // turn on the pullup for button reads. 
  // Note that digitialWrite(9, HIGH) is the same as pinMode(9, INPUT_PULLUP) 
  // Disable pullup on analog pin #9
  digitalWrite(9, LOW);  
  delay(1);
  float measuredvbat = analogRead(pin_BATT);
  measuredvbat = analogRead(pin_BATT);
  // Enable pullup on analog pin #9 (for future reads of BUTTON_A)
  delay(1);
  pinMode(9, INPUT_PULLUP);      
  // NOTE:
  // The M0 (ATSAMD21) pullup-selection register is the same as the
  // output-selection register.  For this reason, you should NOT
  // use the following statements to turn on a pin as an input
  // with a pullup:
  //  pinMode(pin, INPUT)
  //  digitalWrite(pin, HIGH)
  // Instead, to this:
  //  pinMode(pin, INPUT_PULLUP)
  //
  measuredvbat *= 2;    // voltage divided by 2 on board, so multiply back
  measuredvbat *= 3.3;  // Multiply by 3.3V, the reference voltage
  measuredvbat /= 1024; // convert to voltage
  //Serial.print("VBat: " ); Serial.println(measuredvbat);
  display.setTextSize(1);
  display.setCursor(0,24);
  display.print("Batt ");
  display.print(measuredvbat);
  display.print(" V");
} //printLiPoBattV ()

void blinkLED(byte ledPIN){
  //  consumes 300 ms.
  for(int i = 5; i>0; i--){
    digitalWrite(ledPIN, HIGH);
    delay(30);
    digitalWrite(ledPIN, LOW);
    delay(30);
  }    
}

 


Do you need help developing or customizing a IoT product for your needs?   Send me an email requesting a free one hour phone / web share consultation.  

 

The information presented on this website is for the author's use only.   Use of this information by anyone other than the author is offered as guidelines and non-professional advice only.   No liability is assumed by the author or this web site.