Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 6 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
Hand tools and fabrication machines | ||||||
| ||||||
|
Around a month ago, fellow Hackster Mirko Pavleski published his Arduino LED Tester + Resistor Calculator project. This seemed like a useful project to add to my toolbox. I decided to redesign it to reduce its size and also make it portable. Also some of the features like showing the code for the Jaycar Electronic shop wasn't very useful here in Australia so I modified the code to show standard resistor wattages.
The first change was to replace the Arduino Nano with a ATtiny1614 microprocessor. This processor uses very little power and is ideal for a battery powered system. It will easily run on the 3.7V to 4.2V outputted by the LIPO battery. Also the 1602 LCD screen has been replaced by a 128x64 0.96in OLED screen. This helps reduce the size of the unit as well as provide a more detailed display. Finally the four switches are configured as a D-Pad to make adjusting the voltage and current more compact.
Printing of "LED - Box.stl" and "LED - Top.stl" are sliced using a 0.2mm layer height without any supports.
To print "LED - Text.stl", use a 0.2mm layer and switch to a contrasting filament at the start of layer 4.
Use glue or double sided tape to fix the Top and Text prints together.
Printing of "LED - DPad.stl" and "LED - Spacers.stl" are sliced using a 0.1mm layer height without any supports.
AssemblyAs the ATtiny1614 microprocessor only comes in a SMD package a PCB board is necessary. It also supports the switches and OLED display. The Eagle files have been included should you wish to get the board commercially made or you can make it yourself. I used the Toner method to make mine
Start by adding the SMD components. I find it easier to use solder paste rather than use solder from a reel when soldering SMD components. I used my SMD Hot Plate to reflow the solder paste.
Add the links if your board is single-sided.
Next add the pin header for the UPDI programmer. If you use a header on a single sided board, here is the method I use to add them.
Place header on PCB with longer pin side down, solder pins, push black plastic down towards the PCB.
Also add the right-angle 2 pin header that will connect the two 2mm Banana sockets.
Add the JST battery connector also to the copper side of the board.
Add two pins from a straight male header. Remove the plastic and solder the speaker onto the board. (This is optional as I didn't end up adding code for the speaker so you can leave it off along with the 10uF 1206 ceramic capacitor)
Add the four 6mm x 6mm tactile switches with a 6mm shaft.
Add the two pin machined socket.
Add the 8mm x 8mm on/off switch to the PCB
Glue on the display supports
Solder on the OLED display
Add the two 2mm Banana sockets. I soldered a wires to 4mm washers and screwed them on to each socket. Put a female Dupont connector on the other end and plug into the PCB.
Screw the PCB onto the case using 4 x 6mm M3 screws.
Add the battery and close the case.
The ATtiny1614 is part of the new breed of ATtiny microprocessors. Unlike the earlier series such as the ATtiny85, the new breed use the RESET pin to program the CPU. To program it you need a UPDI programmer. I made one using a Arduino Nano. You can find complete build instructions at Create Your Own UPDI Programmer. It also contains the instructions for adding the megaTinyCore boards to your IDE.
Select BOARD: ATtiny3224/1624/1614/1604/824/814/804/424/414/404/...
Select Chip: ATtiny1614
Select Programmer: jtag2updi (megaTinyCore)
Open the sketch and upload it to the ATtiny1614.
Using the LED tester & calculatorBecause this unit runs on a battery, the LIPO battery will change its voltage as it discharges. A fully charged battery is around 4.2V while a discharged battery is around 3.7V. The battery voltage should give you an indication to when the battery needs recharging.
The D-Pad allows you to select the supply voltage that your circuit will use (0 to 99V). You can allow select the how much current should flow through the LED (1 to 20mA). The more current, the brighter the LED will be.
When an LED is connected, it unit will show the voltage drop of the LED as well as the current it is drawing. It will then calculate the resistance that needs to go in series with the LED when you place it in your circuit. It will also provide the minimum wattage for that resistor. The code contains the typical power sizes for both SMD and through hole resistors.
Thanks Mirko for another great project. I think I will find this very useful addition to my toolbox.
UpdateThere has been a software update to fix non-detection of e24 resistor values between 91 and 100. Also the wattage string array has been fixed to display 1W and 2W values correctly.
/*
Arduino LED Tester
Based on How to make Arduino LED Tester + Resistor Calc by mircemk
https://hackaday.io/project/193787-how-to-make-arduino-led-tester-resistor-calc
DPIN--39R--+------+---10R---+--TESTLED---GND
| | |
470u ATOP ABOT
|
GND
Measures LED characteristics by charging up the cap to deliver target current and find forward voltage
From target current, we can calculate R to be used with a design supply voltage and a matching part number.
CPU: ATtiny1614
Display: 0.96" OLED
ATtiny1614 Code: jbrad2089@gmail.com
BOARD: ATtiny1614/1604/814/804/414/404/214/204
Chip: ATtiny1614
Clock Speed: 20MHz
millis()/micros(): "Enabled (default timer)"
Programmer: jtag2updi (megaTinyCore)
ATtiny1614 Pins mapped to Ardunio Pins
+--------+
VCC + 1 14 + GND
(SS) 0 PA4 + 2 13 + PA3 10 (SCK)
1 PA5 + 3 12 + PA2 9 (MISO)
(DAC) 2 PA6 + 4 11 + PA1 8 (MOSI)
3 PA7 + 5 10 + PA0 11 (UPDI)
(RXD) 4 PB3 + 6 9 + PB0 7 (SCL)
(TXD) 5 PB2 + 7 8 + PB1 6 (SDA)
+--------+
V2: First release
V3: Fixed E24 resistor detection for values between 91 & 100
Fixed wattage string in array for 1W resistors
*/
#include "Tiny8kOLED.h"
#include <Wire.h>
#define LCDINT 500 //lcd update interval
#define KEYINT 200 //key check interval
#define OSAMP 16 //Number of samples
//button constants
#define BTN_RG 0 //PA4
#define BTN_LF 1 //PA5
#define BTN_UP 2 //PA6
#define BTN_DN 4 //PB3
#define SPEAKER 3 //PA7
#define DPIN 5 //PB2
#define ABOT 9 //PA2
#define ATOP 10 //PA3
#define ADC_ABOT ADC_MUXPOS_AIN2_gc
#define ADC_ATOP ADC_MUXPOS_AIN3_gc
//Globals for display
int itest = 10; //test current, in mA
long vset = 14000; //display voltage in mV
long vled,vrr,pset; //LED voltage, Resistor voltage, display power
int irr; //resistor current
long atop, abot, supply;
//resistors in Jaycar 1/2 W range, part nos start at RR0524 for 10R
uint8_t e24[] {10,11,12,13,15,16,18,20,22,24,27,30,33,36,39,43,47,51,56,62,68,75,82,91};
#define WATTAGE 12
String watts[] {"1/20","1/16","1/10"," 1/8"," 1/4"," 1/2"," 3/4"," 1"," 2"," 5"," 10"," 20"};
int wattage[] {50,62,100,125,250,500,750,1000,2000,5000,10000,20000};
long lastlcd = 0; //time of last lcd update
long lastkey=0; //time of last key check
long rval; //calculated resistor value for display
long rindex; //index of selected resistor in rvals[]
bool rvalid = false; //flag if resistor value is valid
int pwmout = 0; //pwm output of current driver
//--------------------------------------------------------------------------
// Hardware setup
//--------------------------------------------------------------------------
void setup()
{
pinMode(BTN_LF, INPUT_PULLUP);
pinMode(BTN_RG, INPUT_PULLUP);
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DN, INPUT_PULLUP);
pinMode(SPEAKER, OUTPUT);
pinMode(DPIN, OUTPUT); //pwm pin
//Setup ADC
VREF.CTRLA = VREF_ADC0REFSEL_1V1_gc;
ADC0.CTRLC = ADC_REFSEL_VDDREF_gc | ADC_PRESC_DIV256_gc; // 78kHz clock
ADC0.CTRLA = ADC_ENABLE_bm; // Single, 10-bit
//Display
oled.begin();
}
//--------------------------------------------------------------------------
// Main program loop
//--------------------------------------------------------------------------
void loop()
{
//Read the port analog values
rvalid = false; //set flag to not valid
atop = analogoversample(ADC_ATOP, OSAMP) / OSAMP;
abot = analogoversample(ADC_ABOT, OSAMP) / OSAMP;
long arr = atop - abot; //this is the analog value across the 10R sense resistor
if (arr<0) { arr=0; } //sanity check
//Convert analong values into voltages
//468, 537, 4.56V
supply = round(measureSupplyVoltage() * 1000); //Get supply voltage and convert to (mV)
vled = supply * abot / 1023; //supplyVoltage=1023 => voltage across LED
vrr = supply * arr / 1023; //voltage across sense resistor
irr = vrr / 10; //led and resistor current in mA (cos it's a 10R resistor)
//Adjust DPIN to match requested current
if (irr < itest) { pwmout++; if (pwmout > 255) { pwmout = 255; }} //ramp up current if too low
if (irr > itest) { pwmout--; if (pwmout < 0) { pwmout = 0; }} //ramp down if too high
if (irr > 24) { pwmout -= 5; if (pwmout < 0) { pwmout = 0; }} //ramp down quick if too too high
if (irr > itest*3) { pwmout -= 5; if (pwmout < 0) { pwmout = 0;}} //ramp down quick if too too high
analogWrite(DPIN, pwmout); //output new PWM
//Get the resistor value for the given voltage and current
rval = (vset - vled) / itest; //mV/mV => ohms resistance of display resistor
long mult = 1;
while (rval > 100)
{
mult = mult * 10;
rval = round(rval / 10);
}
for(int i = 0; i < 24; i++)
{
if (e24[i] >= rval)
{
rindex = i;
rval = e24[rindex] * mult;
rvalid = true;
break;
}
}
//V3 - handle case for > 91
if (!rvalid)
{
if (rval >= 95)
{
//Go to the next decade
mult = mult * 10;
rindex = 0;
}
else
{
//Use 91 value
rindex = 23;
}
rval = e24[rindex] * mult;
rvalid = true;
}
rvalid = rvalid && (abs(irr-itest) <= (itest/5)+1); //has current settled within 20%?
rvalid = rvalid && (vled <= vset); //if vled>vset, no valid resistor exists
//work out dissipation in ballast resistor if valid)
pset = (rvalid) ? itest * itest * rval : 0; //this will be in microwatts (milliamps squared)
//check if display due to be updated
if (millis() - lastlcd > LCDINT)
{
lastlcd = millis();
dolcd(); //update display
}
//check if keys due to be checked
if (millis() - lastkey > KEYINT)
{
lastkey = millis();
dobuttons();
}
delay(1);
}
//--------------------------------------------------------------------------
// Update the display
//--------------------------------------------------------------------------
void dolcd()
{
char buffer[10];
oled.setFont(FONT8X16);
oled.setCursor(0,0); //first line
//Battery voltage
sprintf(buffer,"BAT: %2ld\x2e%01ldV ",supply / 1000,(supply / 100) % 10);
oled.print(buffer);
oled.setCursor(0,2); //second line
//Calculator settings
//VLED
sprintf(buffer,"REQ: %2ld\x2e%01ldV ",vset / 1000,(vset / 100) % 10);
oled.print(buffer);
//milliamps
sprintf(buffer,"%2dmA ", itest);
oled.print(buffer);
oled.setCursor(0,4); //third line
//Actual settings
sprintf(buffer,"LED: %2ld\x2e%01ldV ",vled / 1000, (vled / 100) % 10);
oled.print(buffer);
//actual LED current
sprintf(buffer,"%2dmA ", (rvalid) ? irr : 0);
oled.print(buffer);
oled.setCursor(0,6); //third line
//Resistor settings
oled.print("RES: ");
if (rvalid)
{
lcdprintrval(rval); //resistor value (4 characters)
lcdprintwattage(pset);
}
else
{
oled.print("----");
oled.print(" ");
}
}
//--------------------------------------------------------------------------
// Print a value in 10k0 format, always outputs 4 characters
// rval - resistance value to print
//--------------------------------------------------------------------------
void lcdprintrval(long rval)
{
char buffer[10];
long mult=1;
long modval;
if (rval>999) { mult=1000; }
if (rval>999999) { mult=1000000; }
modval = (10 * rval) / mult; //convert to final format, save a decimal place
if (modval>999) //nnnM
{
sprintf(buffer,"%3ld%c",modval/10,getmultchar(mult));
}
else if (modval>99) //nnMn
{
sprintf(buffer,"%2ld%c%1ld",modval/10,getmultchar(mult),modval%10);
}
else //_nMn
{
sprintf(buffer," %1ld%c%1ld",modval/10,getmultchar(mult),modval%10);
}
oled.print(buffer);
}
//--------------------------------------------------------------------------
// Get the multiplier character
// mult - current multiplier (1, 1000, 1000000)
// returns letter for multiplier
//--------------------------------------------------------------------------
char getmultchar(long mult)
{
switch (mult)
{
case 1: return('R'); break;
case 1000: return('k'); break;
case 1000000: return('M'); break;
}
return('?');
}
//--------------------------------------------------------------------------
// Print the wattage
// pset - Watts in micro Watts
//--------------------------------------------------------------------------
void lcdprintwattage(long power)
{
int p = (int)round(power / 1000); //Convert to mW
if (p <= 20000)
{
oled.print(' ');
for(int i = 0; i < WATTAGE; i++)
{
if (wattage[i] >= p)
{
oled.print(watts[i]);
break;
}
}
oled.print('W');
}
else
{
oled.print(" >20W");
}
}
//--------------------------------------------------------------------------
// Test if button is pressed
// Debounces the switch
// pin - pin button is attached to
// waitforrelease - true to wait for button to be released
// returns true if button is pressed
//--------------------------------------------------------------------------
bool button_pressed(int pin, bool waitforrelease)
{
if (digitalRead(pin) == LOW)
{
delay(10);
if (digitalRead(pin) == LOW)
{
if (waitforrelease)
{
while (digitalRead(pin) == LOW)
;
}
return true;
}
}
return false;
}
//--------------------------------------------------------------------------
// Updates variables based on switches
//--------------------------------------------------------------------------
void dobuttons()
{
if (button_pressed(BTN_LF, false)) { itest = max(itest - 1,1); }
if (button_pressed(BTN_RG, false)) { itest = min(itest + 1,20); }
if (button_pressed(BTN_UP, false)) { vset = min(vset + 1000, 99000); }
if (button_pressed(BTN_DN, false)) { vset = max(vset - 1000, 0); }
}
//--------------------------------------------------------------------------
// Read pin samples times and return sum
// adcMux - ADC_Ax constant for the pin to measure
// samples - number of times to read port
// returns sum of samples
//--------------------------------------------------------------------------
long analogoversample(uint8_t adcMux,int samples)
{
long n = 0;
for(int i = 0; i < samples; i++)
{
n = n + measureAnalog(adcMux);
}
return n;
}
//--------------------------------------------------------------------------
// Measure supply voltage
// Source: David Johnson-Davies - www.technoblogy.com - 13th April 2021
// returns supply voltage to ATtiny1614
//--------------------------------------------------------------------------
float measureSupplyVoltage()
{
ADC0.MUXPOS = ADC_MUXPOS_INTREF_gc; // Measure INTREF
ADC0.COMMAND = ADC_STCONV_bm; // Start conversion
while (ADC0.COMMAND & ADC_STCONV_bm); // Wait for completion
uint16_t adc_reading = ADC0.RES; // ADC conversion result
return 1126.4 / adc_reading; // Supply voltage in volts
}
//--------------------------------------------------------------------------
// Measure voltage of a given pin
// adcMux - ADC_Ax constant for the pin to measure
// returns analog port measurement (0 to 1023)
//--------------------------------------------------------------------------
long measureAnalog(uint8_t adcMux)
{
ADC0.MUXPOS = adcMux; // Measure Analog pin
ADC0.COMMAND = ADC_STCONV_bm; // Start conversion
while (ADC0.COMMAND & ADC_STCONV_bm); // Wait for completion
return (long)ADC0.RES; // ADC conversion result
}
/*
* SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
*
* @created: 2014-08-12
* @author: Neven Boyanov
*
* Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
* Modified by M.V. Predoi 2018-09-25
*/
#include <stdint.h>
#include <Arduino.h>
#include <Wire.h>
#include <util/delay.h>
/* Uncomment from the 3 line below this note, the fonts you intend to use:
* FONT6X8 = 764 bytes
* FONT8X16 = 1776 bytes
* FONT16X32= 1334 bytes
*/
//#define FONT6X8 0
#define FONT8X16 1
//#define FONT16X32 2
// ----------------------------------------------------------------------------
#ifndef SSD1306
#define SSD1306 0x3C // Slave address
#endif
// ----------------------------------------------------------------------------
class SSD1306Device: public Print {
public:
SSD1306Device(void);
void begin(void);
void setFont(uint8_t font);
void ssd1306_send_command(uint8_t command);
void ssd1306_send_data_byte(uint8_t byte);
void ssd1306_send_data_start(void);
void ssd1306_send_data_stop(void);
void setCursor(uint8_t x, uint8_t y);
void fill(uint8_t fill);
void clear(void);
void bitmap(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t bitmap[]);
void ssd1306_send_command_start(void);
void ssd1306_send_command_stop(void);
void ssd1306_char_f8x16(uint8_t x, uint8_t y, const char ch[]);
void ssd1306_sleep();
virtual size_t write(byte c);
using Print::write;
};
extern SSD1306Device oled;
// ----------------------------------------------------------------------------
/*
* SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
*
* @created: 2014-08-12
* @author: Neven Boyanov
* @modifications: John Bradnam 2021-05-03
* - modified for ATtiny1614
* - added sleep method
* - rotated display 180%
*/
// ----------------------------------------------------------------------------
#include <stdlib.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/pgmspace.h>
#include "Tiny8kOLED.h"
#ifdef FONT6X8 // In case font6x8 is defined: load it
#include "font6x8.h"
#endif
#ifdef FONT8X16 // In case font8x16 is defined: load it
#include "font8x16.h"
#endif
#ifdef FONT16X32 // In case font16x32 is defined: load it
#include "font16x32.h"
#endif
// ----------------------------------------------------------------------------
#define ROTATE_SSD1306
const uint8_t ssd1306_init_sequence [] PROGMEM = { // Initialization Sequence
0xAE, // Display OFF (sleep mode)
0x20, 0b00, // Set Memory Addressing Mode
// 00=Horizontal Addressing Mode; 01=Vertical Addressing Mode;
// 10=Page Addressing Mode (RESET); 11=Invalid
0xB0, // Set Page Start Address for Page Addressing Mode, 0-7
#ifdef ROTATE_SSD1306
0xC8, // Set COM Output Scan Direction (INC or DEC 0xC0 or 0xC8) <==
#else
0xC0, // Set COM Output Scan Direction (INC or DEC 0xC0 or 0xC8) <==
#endif
0x00, // ---set low column address
0x10, // ---set high column address
0x40, // --set start line address
0x81, 0x3F, // Set contrast control register
#ifdef ROTATE_SSD1306
0xA1, // Set Segment Re-map. (A0=address mapped; A1=address 127 mapped) <==
#else
0xA0, // Set Segment Re-map. (A0=address mapped; A1=address 127 mapped) <==
#endif
0xA6, // Set display mode. A6=Normal; A7=Inverse
0xA8, 0x3F, // Set multiplex ratio(1 to 64)
0xA4, // Output RAM to Display
// 0xA4=Output follows RAM content; 0xA5,Output ignores RAM content
0xD3, 0x00, // Set display offset. 00 = no offset
0xD5, // --set display clock divide ratio/oscillator frequency
0xF0, // --set divide ratio
0xD9, 0x22, // Set pre-charge period
0xDA, 0x12, // Set com pins hardware configuration
0xDB, // --set vcomh
0x20, // 0x20,0.77xVcc
0x8D, 0x14, // Set DC-DC enable
0xAF // Display ON in normal mode
};
uint8_t oledFont, ci, oledX, oledY;
SSD1306Device::SSD1306Device(void){}
void SSD1306Device::begin(void)
{
Wire.begin();
for (uint8_t i = 0; i < sizeof (ssd1306_init_sequence); i++) {
ssd1306_send_command(pgm_read_byte(&ssd1306_init_sequence[i]));
}
clear();
}
void SSD1306Device::setFont(uint8_t font)
{
oledFont = font;
}
void SSD1306Device::ssd1306_send_command_start(void) {
Wire.beginTransmission(SSD1306);
Wire.write(0x00); // write command
}
void SSD1306Device::ssd1306_send_command_stop(void) {
Wire.endTransmission();
}
void SSD1306Device::ssd1306_send_data_byte(uint8_t byte)
{
/*
//if(Wire.writeAvailable()){
ssd1306_send_data_stop();
ssd1306_send_data_start();
//}
*/
Wire.write(byte);
}
void SSD1306Device::ssd1306_send_command(uint8_t command)
{
ssd1306_send_command_start();
Wire.write(command);
ssd1306_send_command_stop();
}
void SSD1306Device::ssd1306_send_data_start(void)
{
Wire.beginTransmission(SSD1306);
Wire.write(0x40); //write data
}
void SSD1306Device::ssd1306_send_data_stop(void)
{
Wire.endTransmission();
}
void SSD1306Device::setCursor(uint8_t x, uint8_t y)
{
ssd1306_send_command_start();
Wire.write(0xb0 + y);
Wire.write(((x & 0xf0) >> 4) | 0x10); // | 0x10
Wire.write((x & 0x0f) | 0x01); // | 0x01
ssd1306_send_command_stop();
oledX = x;
oledY = y;
}
void SSD1306Device::clear(void)
{
fill(0x00);
}
void SSD1306Device::fill(uint8_t fill)
{
uint8_t m,n,o;
for (m = 0; m < 8; m++)
{
ssd1306_send_command(0xb0 + m); // page0 - page1
ssd1306_send_command(0x00); // low column start address
ssd1306_send_command(0x10); // high column start address
ssd1306_send_data_start();
for (n = 0, o = 0; n < 128; n++)
{
ssd1306_send_data_byte(fill);
o++;
if (o == BUFFER_LENGTH - 1)
{
ssd1306_send_data_stop();
ssd1306_send_data_start();
o = 0;
}
}
ssd1306_send_data_stop();
}
setCursor(0, 0);
}
size_t SSD1306Device::write(byte c) {
// This function is modified to work also with 16x32 fonts for digits
// Useful to display only numerical results in Largest Size!
//=================================================================
#ifdef FONT16X32
if (oledFont==2) {
// If "font16x32.h" contains only 10 symbols (0..9) rotated 90deg. right, then:
ci = c - 44; // Only symbols ,-./0...9: are available in 13kB file [0=char(16)]
if (oledX > 112){ // long numbers are cut to max 8 digits
return 1;
}
for (uint8_t j = 3; j > 1 ; j--) { // Send from top to bottom 2*16 bytes on rows
ssd1306_send_data_start();
for (uint8_t i = 0; i < 16; i++) { // Send 16 bytes as rows, at the same y position
Wire.write(pgm_read_byte(&ssd1306xled_font16x32[ci * 64 + 4*i +j]));
}
ssd1306_send_data_stop();
setCursor(oledX, oledY + 4-j); // Prepare position of next row of 16 bytes under the first one
}
setCursor(oledX, oledY-1); // Return a row higher
for (uint8_t j = 3; j > 1 ; j--) { // Send from the lower half 2*16 bytes on rows
ssd1306_send_data_start();
for (uint8_t i = 0; i < 16; i++) { // Send from top to bottom 2*16 bytes
Wire.write(pgm_read_byte(&ssd1306xled_font16x32[ci * 64 + 4*i +j -2]));
}
ssd1306_send_data_stop();
setCursor(oledX, oledY+1); // Relative move lower by 1 byte for last 16 bytes
}
setCursor(oledX+16, oledY-4); // Move cursor for next digit
}
#endif
//==================================================================
#ifdef FONT8X16
if(oledFont==1) {
if (c == '\n') {
oledY++;
if (oledY > 6) {
oledY = 6;
}
setCursor(0, oledY);
return 1;
}
ci = c - 32;
if (oledX > 120) {
oledY+=2;
if (oledY > 6) {
oledY = 6;
}
setCursor(0, oledY);
}
ssd1306_send_data_start();
for (uint8_t i = 0; i < 8; i++) {
Wire.write(pgm_read_byte(&ssd1306xled_font8x16[ci * 16 + i]));
}
ssd1306_send_data_stop();
setCursor(oledX, oledY + 1);
ssd1306_send_data_start();
for (uint8_t i = 0; i < 8; i++) {
Wire.write(pgm_read_byte(&ssd1306xled_font8x16[ci * 16 + i + 8]));
}
ssd1306_send_data_stop();
setCursor(oledX + 8, oledY - 1);
}
#endif
//==================================================================
#ifdef FONT6X8
if(oledFont== 0) {
if (c == '\n') {
if (oledFont == FONT6X8) {
oledY++;
if (oledY > 7) {
oledY = 7;
}
setCursor(0, oledY);
return 1;
}
}
ci = c - 32;
if (oledX > 122) {
oledY++;
if (oledY > 7) {
oledY = 7;
}
setCursor(0, oledY);
}
ssd1306_send_data_start();
for (uint8_t i= 0; i < 6; i++) {
ssd1306_send_data_byte(pgm_read_byte(&ssd1306xled_font6x8[ci * 6 + i]));
}
ssd1306_send_data_stop();
oledX+=6; // we don't need to call setCursor for every character.
}
#endif
//==================================================================
return 1;
}
void SSD1306Device::bitmap(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, const uint8_t bitmap[])
{
uint16_t j = 0;
uint8_t y, x;
// if (y1 % 8 == 0) y = y1 / 8; // else y = y1 / 8 + 1; // tBUG :: this does nothing as y is initialized below
// THIS PARAM rule on y makes any adjustment here WRONG //usage oled.bitmap(START X IN PIXELS, START Y IN ROWS OF 8 PIXELS, END X IN PIXELS, END Y IN ROWS OF 8 PIXELS, IMAGE ARRAY);
for (y = y0; y < y1; y++)
{
setCursor(x0,y);
ssd1306_send_data_start();
for (x = x0; x < x1; x++)
{
ssd1306_send_data_byte(pgm_read_byte(&bitmap[j++]));
}
ssd1306_send_data_stop();
}
setCursor(0, 0);
}
//--------------------- Extra -------------------------------
//Shut down OLED and put ATtiny to sleep
//Will wake up when LEFT button is pressed
void SSD1306Device::ssd1306_sleep()
{
clear();
ssd1306_send_command(0xAE);
//cbi(ADCSRA,ADEN); // switch Analog to Digitalconverter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_enable();
sleep_mode(); // System actually sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
//sbi(ADCSRA,ADEN); // switch Analog to Digitalconverter ON
ssd1306_send_command(0xAF);
}
SSD1306Device oled;
// ----------------------------------------------------------------------------
/*
* SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
*
* @created: 2014-08-12
* @author: Neven Boyanov
*
* Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
*
*/
// ----------------------------------------------------------------------------
#include <avr/pgmspace.h>
// ----------------------------------------------------------------------------
/* Standard ASCII 8x16 font */
const uint8_t ssd1306xled_font8x16 [] PROGMEM = {
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0
0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00, // ! 1
0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // " 2
0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00, // # 3
0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00, // $ 4
0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00, // % 5
0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10, // & 6
0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ' 7
0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00, // ( 8
0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00, // ) 9
0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00, // * 10
0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00, // + 11
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00, // , 12
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01, // - 13
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00, // . 14
0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00, // / 15
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00, // 0 16
0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // 1 17
0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00, // 2 18
0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00, // 3 19
0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00, // 4 20
0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00, // 5 21
0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00, // 6 22
0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00, // 7 23
0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00, // 8 24
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00, // 9 25
0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00, // : 26
0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00, // ; 27
0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00, // < 28
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00, // = 29
0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00, // > 30
0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00, // ? 31
0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00, // @ 32
0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20, // A 33
0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00, // B 34
0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00, // C 35
0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00, // D 36
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00, // E 37
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00, // F 38
0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00, // G 39
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20, // H 40
0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // I 41
0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00, // J 42
0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00, // K 43
0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00, // L 44
0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00, // M 45
0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00, // N 46
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00, // O 47
0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00, // P 48
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00, // Q 49
0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20, // R 50
0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00, // S 51
0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00, // T 52
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00, // U 53
0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00, // V 54
0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00, // W 55
0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20, // X 56
0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00, // Y 57
0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00, // Z 58
0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00, // [ 59
0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00, // \ 60
0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00, // ] 61
0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ^ 62
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, // _ 63
0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ` 64
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20, // a 65
0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00, // b 66
0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00, // c 67
0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20, // d 68
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00, // e 69
0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // f 70
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00, // g 71
0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20, // h 72
0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // i 73
0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00, // j 74
0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00, // k 75
0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00, // l 76
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F, // m 77
0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20, // n 78
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00, // o 79
0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00, // p 80
0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80, // q 81
0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00, // r 82
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00, // s 83
0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00, // t 84
0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20, // u 85
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00, // v 86
0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00, // w 87
0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00, // x 88
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00, // y 89
0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00, // z 90
0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40, // { 91
0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00, // | 92
0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00, // } 93
0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ~ 94
};
// ----------------------------------------------------------------------------
Comments