Components and supplies
Pushbutton Switch, Momentary
Arduino Nano R3
i2c 0.96" OLED display (SSD1306)
Through Hole Resistor, 100 kohm
Apps and platforms
Arduino IDE
Project description
Code
Display Source Code (.cpp)
arduino
download and add these two files to the sketch folder if you display doesn't work.
1/********************************************************************* 2This is a library for our Monochrome OLEDs based on SSD1306 drivers 3 4 Pick one up today in the adafruit shop! 5 ------> http://www.adafruit.com/category/63_98 6 7These displays use SPI to communicate, 4 or 5 pins are required to 8interface 9 10Adafruit invests time and resources providing this open source code, 11please support Adafruit and open-source hardware by purchasing 12products from Adafruit! 13 14Written by Limor Fried/Ladyada for Adafruit Industries. 15BSD license, check license.txt for more information 16All text above, and the splash screen below must be included in any redistribution 17*********************************************************************/ 18 19#ifdef __AVR__ 20 #include <avr/pgmspace.h> 21#elif defined(ESP8266) || defined(ESP32) 22 #include <pgmspace.h> 23#else 24 #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) 25#endif 26 27#if !defined(__ARM_ARCH) && !defined(ENERGIA) && !defined(ESP8266) && !defined(ESP32) && !defined(__arc__) 28 #include <util/delay.h> 29#endif 30 31#include <stdlib.h> 32 33#include <Wire.h> 34#include <SPI.h> 35#include "Adafruit_GFX.h" 36#include "Adafruit_SSD1306.h" 37 38// the memory buffer for the LCD 39 40static uint8_t buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8] = { 410x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 420x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 430x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 440x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 450x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 460x00, 0x80, 0x80, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 470x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 480x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 490x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 500x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00, 510x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 520x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0xFF, 53#if (SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH > 96*16) 540xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 550x80, 0xFF, 0xFF, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 560x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x8C, 0x8E, 0x84, 0x00, 0x00, 0x80, 0xF8, 570xF8, 0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 580xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80, 590x00, 0xE0, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 600x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0x01, 0x01, 610x01, 0x01, 0x83, 0xFF, 0xFF, 0x00, 0x00, 0x7C, 0xFE, 0xC7, 0x01, 0x01, 0x01, 0x01, 0x83, 0xFF, 620xFF, 0xFF, 0x00, 0x38, 0xFE, 0xC7, 0x83, 0x01, 0x01, 0x01, 0x83, 0xC7, 0xFF, 0xFF, 0x00, 0x00, 630x01, 0xFF, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x07, 0x01, 0x01, 0x01, 0x00, 0x00, 0x7F, 0xFF, 640x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, 650xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 660x03, 0x0F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC7, 0xC7, 0x8F, 670x8F, 0x9F, 0xBF, 0xFF, 0xFF, 0xC3, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFC, 680xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x01, 0x03, 0x03, 0x03, 690x03, 0x03, 0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 700x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x03, 0x03, 0x00, 0x00, 710x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 720x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 730x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74#if (SSD1306_LCDHEIGHT == 64) 750x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x1F, 0x0F, 760x87, 0xC7, 0xF7, 0xFF, 0xFF, 0x1F, 0x1F, 0x3D, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0x7C, 0x7D, 0xFF, 770xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x0F, 0x07, 0x00, 0x30, 0x30, 0x00, 0x00, 780x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 790x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 800x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0x00, 810x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 820x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 830x00, 0xC0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x1F, 840x0F, 0x07, 0x1F, 0x7F, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0, 850x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 860x00, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xF0, 0xF8, 0x1C, 0x0E, 870x06, 0x06, 0x06, 0x0C, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFC, 880xFE, 0xFC, 0x00, 0x18, 0x3C, 0x7E, 0x66, 0xE6, 0xCE, 0x84, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0x06, 890x06, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x06, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0xC0, 0xF8, 900xFC, 0x4E, 0x46, 0x46, 0x46, 0x4E, 0x7C, 0x78, 0x40, 0x18, 0x3C, 0x76, 0xE6, 0xCE, 0xCC, 0x80, 910x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 920x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x03, 930x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 940x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x07, 0x0E, 0x0C, 950x18, 0x18, 0x0C, 0x06, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x0F, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 960x07, 0x01, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 970x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x07, 980x07, 0x0C, 0x0C, 0x18, 0x1C, 0x0C, 0x06, 0x06, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07, 990x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1000x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1010x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1020x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1030x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1040x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1050x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1060x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 107#endif 108#endif 109}; 110 111#define ssd1306_swap(a, b) { int16_t t = a; a = b; b = t; } 112 113// the most basic function, set a single pixel 114void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) { 115 if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) 116 return; 117 118 // check rotation, move pixel around if necessary 119 switch (getRotation()) { 120 case 1: 121 ssd1306_swap(x, y); 122 x = WIDTH - x - 1; 123 break; 124 case 2: 125 x = WIDTH - x - 1; 126 y = HEIGHT - y - 1; 127 break; 128 case 3: 129 ssd1306_swap(x, y); 130 y = HEIGHT - y - 1; 131 break; 132 } 133 134 // x is which column 135 switch (color) 136 { 137 case WHITE: buffer[x+ (y/8)*SSD1306_LCDWIDTH] |= (1 << (y&7)); break; 138 case BLACK: buffer[x+ (y/8)*SSD1306_LCDWIDTH] &= ~(1 << (y&7)); break; 139 case INVERSE: buffer[x+ (y/8)*SSD1306_LCDWIDTH] ^= (1 << (y&7)); break; 140 } 141 142} 143 144Adafruit_SSD1306::Adafruit_SSD1306(int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS) : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) { 145 cs = CS; 146 rst = RST; 147 dc = DC; 148 sclk = SCLK; 149 sid = SID; 150 hwSPI = false; 151} 152 153// constructor for hardware SPI - we indicate DataCommand, ChipSelect, Reset 154Adafruit_SSD1306::Adafruit_SSD1306(int8_t DC, int8_t RST, int8_t CS) : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) { 155 dc = DC; 156 rst = RST; 157 cs = CS; 158 hwSPI = true; 159} 160 161// initializer for I2C - we only indicate the reset pin! 162Adafruit_SSD1306::Adafruit_SSD1306(int8_t reset) : 163Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) { 164 sclk = dc = cs = sid = -1; 165 rst = reset; 166} 167 168 169void Adafruit_SSD1306::begin(uint8_t vccstate, uint8_t i2caddr, bool reset) { 170 _vccstate = vccstate; 171 _i2caddr = i2caddr; 172 173 // set pin directions 174 if (sid != -1){ 175 pinMode(dc, OUTPUT); 176 pinMode(cs, OUTPUT); 177#ifdef HAVE_PORTREG 178 csport = portOutputRegister(digitalPinToPort(cs)); 179 cspinmask = digitalPinToBitMask(cs); 180 dcport = portOutputRegister(digitalPinToPort(dc)); 181 dcpinmask = digitalPinToBitMask(dc); 182#endif 183 if (!hwSPI){ 184 // set pins for software-SPI 185 pinMode(sid, OUTPUT); 186 pinMode(sclk, OUTPUT); 187#ifdef HAVE_PORTREG 188 clkport = portOutputRegister(digitalPinToPort(sclk)); 189 clkpinmask = digitalPinToBitMask(sclk); 190 mosiport = portOutputRegister(digitalPinToPort(sid)); 191 mosipinmask = digitalPinToBitMask(sid); 192#endif 193 } 194 if (hwSPI){ 195 SPI.begin(); 196#ifdef SPI_HAS_TRANSACTION 197 SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); 198#else 199 SPI.setClockDivider (4); 200#endif 201 } 202 } 203 else 204 { 205 // I2C Init 206 Wire.begin(); 207#ifdef __SAM3X8E__ 208 // Force 400 KHz I2C, rawr! (Uses pins 20, 21 for SDA, SCL) 209 TWI1->TWI_CWGR = 0; 210 TWI1->TWI_CWGR = ((VARIANT_MCK / (2 * 400000)) - 4) * 0x101; 211#endif 212 } 213 if ((reset) && (rst >= 0)) { 214 // Setup reset pin direction (used by both SPI and I2C) 215 pinMode(rst, OUTPUT); 216 digitalWrite(rst, HIGH); 217 // VDD (3.3V) goes high at start, lets just chill for a ms 218 delay(1); 219 // bring reset low 220 digitalWrite(rst, LOW); 221 // wait 10ms 222 delay(10); 223 // bring out of reset 224 digitalWrite(rst, HIGH); 225 // turn on VCC (9V?) 226 } 227 228 // Init sequence 229 ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE 230 ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 231 ssd1306_command(0x80); // the suggested ratio 0x80 232 233 ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8 234 ssd1306_command(SSD1306_LCDHEIGHT - 1); 235 236 ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3 237 ssd1306_command(0x0); // no offset 238 ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 239 ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D 240 if (vccstate == SSD1306_EXTERNALVCC) 241 { ssd1306_command(0x10); } 242 else 243 { ssd1306_command(0x14); } 244 ssd1306_command(SSD1306_MEMORYMODE); // 0x20 245 ssd1306_command(0x00); // 0x0 act like ks0108 246 ssd1306_command(SSD1306_SEGREMAP | 0x1); 247 ssd1306_command(SSD1306_COMSCANDEC); 248 249 #if defined SSD1306_128_32 250 ssd1306_command(SSD1306_SETCOMPINS); // 0xDA 251 ssd1306_command(0x02); 252 ssd1306_command(SSD1306_SETCONTRAST); // 0x81 253 ssd1306_command(0x8F); 254 255#elif defined SSD1306_128_64 256 ssd1306_command(SSD1306_SETCOMPINS); // 0xDA 257 ssd1306_command(0x12); 258 ssd1306_command(SSD1306_SETCONTRAST); // 0x81 259 if (vccstate == SSD1306_EXTERNALVCC) 260 { ssd1306_command(0x9F); } 261 else 262 { ssd1306_command(0xCF); } 263 264#elif defined SSD1306_96_16 265 ssd1306_command(SSD1306_SETCOMPINS); // 0xDA 266 ssd1306_command(0x2); //ada x12 267 ssd1306_command(SSD1306_SETCONTRAST); // 0x81 268 if (vccstate == SSD1306_EXTERNALVCC) 269 { ssd1306_command(0x10); } 270 else 271 { ssd1306_command(0xAF); } 272 273#endif 274 275 ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9 276 if (vccstate == SSD1306_EXTERNALVCC) 277 { ssd1306_command(0x22); } 278 else 279 { ssd1306_command(0xF1); } 280 ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB 281 ssd1306_command(0x40); 282 ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4 283 ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6 284 285 ssd1306_command(SSD1306_DEACTIVATE_SCROLL); 286 287 ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel 288} 289 290 291void Adafruit_SSD1306::invertDisplay(uint8_t i) { 292 if (i) { 293 ssd1306_command(SSD1306_INVERTDISPLAY); 294 } else { 295 ssd1306_command(SSD1306_NORMALDISPLAY); 296 } 297} 298 299void Adafruit_SSD1306::ssd1306_command(uint8_t c) { 300 if (sid != -1) 301 { 302 // SPI 303#ifdef HAVE_PORTREG 304 *csport |= cspinmask; 305 *dcport &= ~dcpinmask; 306 *csport &= ~cspinmask; 307#else 308 digitalWrite(cs, HIGH); 309 digitalWrite(dc, LOW); 310 digitalWrite(cs, LOW); 311#endif 312 fastSPIwrite(c); 313#ifdef HAVE_PORTREG 314 *csport |= cspinmask; 315#else 316 digitalWrite(cs, HIGH); 317#endif 318 } 319 else 320 { 321 // I2C 322 uint8_t control = 0x00; // Co = 0, D/C = 0 323 Wire.beginTransmission(_i2caddr); 324 Wire.write(control); 325 Wire.write(c); 326 Wire.endTransmission(); 327 } 328} 329 330// startscrollright 331// Activate a right handed scroll for rows start through stop 332// Hint, the display is 16 rows tall. To scroll the whole display, run: 333// display.scrollright(0x00, 0x0F) 334void Adafruit_SSD1306::startscrollright(uint8_t start, uint8_t stop){ 335 ssd1306_command(SSD1306_RIGHT_HORIZONTAL_SCROLL); 336 ssd1306_command(0X00); 337 ssd1306_command(start); 338 ssd1306_command(0X00); 339 ssd1306_command(stop); 340 ssd1306_command(0X00); 341 ssd1306_command(0XFF); 342 ssd1306_command(SSD1306_ACTIVATE_SCROLL); 343} 344 345// startscrollleft 346// Activate a right handed scroll for rows start through stop 347// Hint, the display is 16 rows tall. To scroll the whole display, run: 348// display.scrollright(0x00, 0x0F) 349void Adafruit_SSD1306::startscrollleft(uint8_t start, uint8_t stop){ 350 ssd1306_command(SSD1306_LEFT_HORIZONTAL_SCROLL); 351 ssd1306_command(0X00); 352 ssd1306_command(start); 353 ssd1306_command(0X00); 354 ssd1306_command(stop); 355 ssd1306_command(0X00); 356 ssd1306_command(0XFF); 357 ssd1306_command(SSD1306_ACTIVATE_SCROLL); 358} 359 360// startscrolldiagright 361// Activate a diagonal scroll for rows start through stop 362// Hint, the display is 16 rows tall. To scroll the whole display, run: 363// display.scrollright(0x00, 0x0F) 364void Adafruit_SSD1306::startscrolldiagright(uint8_t start, uint8_t stop){ 365 ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); 366 ssd1306_command(0X00); 367 ssd1306_command(SSD1306_LCDHEIGHT); 368 ssd1306_command(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL); 369 ssd1306_command(0X00); 370 ssd1306_command(start); 371 ssd1306_command(0X00); 372 ssd1306_command(stop); 373 ssd1306_command(0X01); 374 ssd1306_command(SSD1306_ACTIVATE_SCROLL); 375} 376 377// startscrolldiagleft 378// Activate a diagonal scroll for rows start through stop 379// Hint, the display is 16 rows tall. To scroll the whole display, run: 380// display.scrollright(0x00, 0x0F) 381void Adafruit_SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop){ 382 ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); 383 ssd1306_command(0X00); 384 ssd1306_command(SSD1306_LCDHEIGHT); 385 ssd1306_command(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL); 386 ssd1306_command(0X00); 387 ssd1306_command(start); 388 ssd1306_command(0X00); 389 ssd1306_command(stop); 390 ssd1306_command(0X01); 391 ssd1306_command(SSD1306_ACTIVATE_SCROLL); 392} 393 394void Adafruit_SSD1306::stopscroll(void){ 395 ssd1306_command(SSD1306_DEACTIVATE_SCROLL); 396} 397 398// Dim the display 399// dim = true: display is dimmed 400// dim = false: display is normal 401void Adafruit_SSD1306::dim(boolean dim) { 402 uint8_t contrast; 403 404 if (dim) { 405 contrast = 1; //0; // Dimmed display - If = zero, the display is off. 406 } else { 407 if (_vccstate == SSD1306_EXTERNALVCC) { 408 contrast = 0x9F; 409 } else { 410 contrast = 0xCF; 411 } 412 } 413 // the range of contrast to too small to be really useful 414 // it is useful to dim the display 415 ssd1306_command(SSD1306_SETCONTRAST); 416 ssd1306_command(contrast); 417} 418 419void Adafruit_SSD1306::display(void) { 420 ssd1306_command(SSD1306_COLUMNADDR); 421 ssd1306_command(0); // Column start address (0 = reset) 422 ssd1306_command(SSD1306_LCDWIDTH-1); // Column end address (127 = reset) 423 424 ssd1306_command(SSD1306_PAGEADDR); 425 ssd1306_command(0); // Page start address (0 = reset) 426 #if SSD1306_LCDHEIGHT == 64 427 ssd1306_command(7); // Page end address 428 #endif 429 #if SSD1306_LCDHEIGHT == 32 430 ssd1306_command(3); // Page end address 431 #endif 432 #if SSD1306_LCDHEIGHT == 16 433 ssd1306_command(1); // Page end address 434 #endif 435 436 if (sid != -1) 437 { 438 // SPI 439#ifdef HAVE_PORTREG 440 *csport |= cspinmask; 441 *dcport |= dcpinmask; 442 *csport &= ~cspinmask; 443#else 444 digitalWrite(cs, HIGH); 445 digitalWrite(dc, HIGH); 446 digitalWrite(cs, LOW); 447#endif 448 449 for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { 450 fastSPIwrite(buffer[i]); 451 } 452#ifdef HAVE_PORTREG 453 *csport |= cspinmask; 454#else 455 digitalWrite(cs, HIGH); 456#endif 457 } 458 else 459 { 460 // save I2C bitrate 461#ifdef TWBR 462 uint8_t twbrbackup = TWBR; 463 TWBR = 12; // upgrade to 400KHz! 464#endif 465 466 //Serial.println(TWBR, DEC); 467 //Serial.println(TWSR & 0x3, DEC); 468 469 // I2C 470 for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { 471 // send a bunch of data in one xmission 472 Wire.beginTransmission(_i2caddr); 473 WIRE_WRITE(0x40); 474 for (uint8_t x=0; x<16; x++) { 475 WIRE_WRITE(buffer[i]); 476 i++; 477 } 478 i--; 479 Wire.endTransmission(); 480 } 481#ifdef TWBR 482 TWBR = twbrbackup; 483#endif 484 } 485} 486 487// clear everything 488void Adafruit_SSD1306::clearDisplay(void) { 489 memset(buffer, 0, (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8)); 490} 491 492 493inline void Adafruit_SSD1306::fastSPIwrite(uint8_t d) { 494 495 if(hwSPI) { 496 (void)SPI.transfer(d); 497 } else { 498 for(uint8_t bit = 0x80; bit; bit >>= 1) { 499#ifdef HAVE_PORTREG 500 *clkport &= ~clkpinmask; 501 if(d & bit) *mosiport |= mosipinmask; 502 else *mosiport &= ~mosipinmask; 503 *clkport |= clkpinmask; 504#else 505 digitalWrite(sclk, LOW); 506 if(d & bit) digitalWrite(sid, HIGH); 507 else digitalWrite(sid, LOW); 508 digitalWrite(sclk, HIGH); 509#endif 510 } 511 } 512} 513 514void Adafruit_SSD1306::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { 515 boolean bSwap = false; 516 switch(rotation) { 517 case 0: 518 // 0 degree rotation, do nothing 519 break; 520 case 1: 521 // 90 degree rotation, swap x & y for rotation, then invert x 522 bSwap = true; 523 ssd1306_swap(x, y); 524 x = WIDTH - x - 1; 525 break; 526 case 2: 527 // 180 degree rotation, invert x and y - then shift y around for height. 528 x = WIDTH - x - 1; 529 y = HEIGHT - y - 1; 530 x -= (w-1); 531 break; 532 case 3: 533 // 270 degree rotation, swap x & y for rotation, then invert y and adjust y for w (not to become h) 534 bSwap = true; 535 ssd1306_swap(x, y); 536 y = HEIGHT - y - 1; 537 y -= (w-1); 538 break; 539 } 540 541 if(bSwap) { 542 drawFastVLineInternal(x, y, w, color); 543 } else { 544 drawFastHLineInternal(x, y, w, color); 545 } 546} 547 548void Adafruit_SSD1306::drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) { 549 // Do bounds/limit checks 550 if(y < 0 || y >= HEIGHT) { return; } 551 552 // make sure we don't try to draw below 0 553 if(x < 0) { 554 w += x; 555 x = 0; 556 } 557 558 // make sure we don't go off the edge of the display 559 if( (x + w) > WIDTH) { 560 w = (WIDTH - x); 561 } 562 563 // if our width is now negative, punt 564 if(w <= 0) { return; } 565 566 // set up the pointer for movement through the buffer 567 register uint8_t *pBuf = buffer; 568 // adjust the buffer pointer for the current row 569 pBuf += ((y/8) * SSD1306_LCDWIDTH); 570 // and offset x columns in 571 pBuf += x; 572 573 register uint8_t mask = 1 << (y&7); 574 575 switch (color) 576 { 577 case WHITE: while(w--) { *pBuf++ |= mask; }; break; 578 case BLACK: mask = ~mask; while(w--) { *pBuf++ &= mask; }; break; 579 case INVERSE: while(w--) { *pBuf++ ^= mask; }; break; 580 } 581} 582 583void Adafruit_SSD1306::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { 584 bool bSwap = false; 585 switch(rotation) { 586 case 0: 587 break; 588 case 1: 589 // 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w) 590 bSwap = true; 591 ssd1306_swap(x, y); 592 x = WIDTH - x - 1; 593 x -= (h-1); 594 break; 595 case 2: 596 // 180 degree rotation, invert x and y - then shift y around for height. 597 x = WIDTH - x - 1; 598 y = HEIGHT - y - 1; 599 y -= (h-1); 600 break; 601 case 3: 602 // 270 degree rotation, swap x & y for rotation, then invert y 603 bSwap = true; 604 ssd1306_swap(x, y); 605 y = HEIGHT - y - 1; 606 break; 607 } 608 609 if(bSwap) { 610 drawFastHLineInternal(x, y, h, color); 611 } else { 612 drawFastVLineInternal(x, y, h, color); 613 } 614} 615 616 617void Adafruit_SSD1306::drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h, uint16_t color) { 618 619 // do nothing if we're off the left or right side of the screen 620 if(x < 0 || x >= WIDTH) { return; } 621 622 // make sure we don't try to draw below 0 623 if(__y < 0) { 624 // __y is negative, this will subtract enough from __h to account for __y being 0 625 __h += __y; 626 __y = 0; 627 628 } 629 630 // make sure we don't go past the height of the display 631 if( (__y + __h) > HEIGHT) { 632 __h = (HEIGHT - __y); 633 } 634 635 // if our height is now negative, punt 636 if(__h <= 0) { 637 return; 638 } 639 640 // this display doesn't need ints for coordinates, use local byte registers for faster juggling 641 register uint8_t y = __y; 642 register uint8_t h = __h; 643 644 645 // set up the pointer for fast movement through the buffer 646 register uint8_t *pBuf = buffer; 647 // adjust the buffer pointer for the current row 648 pBuf += ((y/8) * SSD1306_LCDWIDTH); 649 // and offset x columns in 650 pBuf += x; 651 652 // do the first partial byte, if necessary - this requires some masking 653 register uint8_t mod = (y&7); 654 if(mod) { 655 // mask off the high n bits we want to set 656 mod = 8-mod; 657 658 // note - lookup table results in a nearly 10% performance improvement in fill* functions 659 // register uint8_t mask = ~(0xFF >> (mod)); 660 static uint8_t premask[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; 661 register uint8_t mask = premask[mod]; 662 663 // adjust the mask if we're not going to reach the end of this byte 664 if( h < mod) { 665 mask &= (0XFF >> (mod-h)); 666 } 667 668 switch (color) 669 { 670 case WHITE: *pBuf |= mask; break; 671 case BLACK: *pBuf &= ~mask; break; 672 case INVERSE: *pBuf ^= mask; break; 673 } 674 675 // fast exit if we're done here! 676 if(h<mod) { return; } 677 678 h -= mod; 679 680 pBuf += SSD1306_LCDWIDTH; 681 } 682 683 684 // write solid bytes while we can - effectively doing 8 rows at a time 685 if(h >= 8) { 686 if (color == INVERSE) { // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop 687 do { 688 *pBuf=~(*pBuf); 689 690 // adjust the buffer forward 8 rows worth of data 691 pBuf += SSD1306_LCDWIDTH; 692 693 // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now) 694 h -= 8; 695 } while(h >= 8); 696 } 697 else { 698 // store a local value to work with 699 register uint8_t val = (color == WHITE) ? 255 : 0; 700 701 do { 702 // write our value in 703 *pBuf = val; 704 705 // adjust the buffer forward 8 rows worth of data 706 pBuf += SSD1306_LCDWIDTH; 707 708 // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now) 709 h -= 8; 710 } while(h >= 8); 711 } 712 } 713 714 // now do the final partial byte, if necessary 715 if(h) { 716 mod = h & 7; 717 // this time we want to mask the low bits of the byte, vs the high bits we did above 718 // register uint8_t mask = (1 << mod) - 1; 719 // note - lookup table results in a nearly 10% performance improvement in fill* functions 720 static uint8_t postmask[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; 721 register uint8_t mask = postmask[mod]; 722 switch (color) 723 { 724 case WHITE: *pBuf |= mask; break; 725 case BLACK: *pBuf &= ~mask; break; 726 case INVERSE: *pBuf ^= mask; break; 727 } 728 } 729} 730
TicTacToe - OLED version (no resistors)
arduino
This uses the Arduino internal pullup resistors
1/************************************************************************** 2 * 3 * TicTacToe - Oled - No pulldown resistors version 4 * 5 * It requires an Arduino Nano, Uno, Mini Pro, ... 6 * 7 * Hardware connections: 8 * DISPLAY - Arduino Nano/Uno 9 * GND - GND 10 * VDD - 3.3V depending on your model 11 * SCL - A5 12 * SDA - A4 13 * 14 * Arduino pin 2 <-- button MOVE <--+----- GND 15 * Arduino pin 3 <-- button OK <--+ 16 * 17 * Note: No need to use resistors, we use the internal pullup resistors 18 * 19 * Install the Adafruit SSD1306 libraries 20 * by Arduino IDE, menu Tools -> Manage Libraries 21 * 22 * apr/2022, Giovanni Verrua 23**************************************************************************/ 24 25//including the needed libraries for the OLED display 26#include <SPI.h> 27#include <Wire.h> 28#include <Adafruit_GFX.h> 29#include <Adafruit_SSD1306.h> 30 31#define BUTTON_MOVE 2 32#define BUTTON_OK 3 33 34// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) 35#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) 36Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); 37 38 39int gameStatus = 0; 40int whosplaying = 0; //0 = Arduino, 1 = Human 41 42int winner = -1; //-1 = Playing, 0 = Draw, 1 = Human, 2 = CPU 43 44 45int board[]={0,0,0, 46 0,0,0, 47 0,0,0}; //0 = blank, 1 = human (circle), 2 = computer (cross) 48 49 50 51 52 53 54//-------------------------------------------------------------------------------------------------------- 55void playhuman() { 56 57 int humanMove = 0; 58 59 bool stayInLoop = true; 60 bool showDot = false; 61 long timerPos = millis()-1000; 62 63 64 while (stayInLoop) { //stay in loop until the player makes his/her choice hitting the OK button. 65 66 //If the current "?" position isn't avaliable (the cell value is 1 or 2), this loop will 67 //move the "?" to the next free cell. 68 //NOTE: there must be at least one empty cell (or it will never exit from this loop [deadlock]). 69 //This is granted, because if all the cells are used, there's a winner or it's a draft. 70 //(the calling function [loop function] check it before to continue). 71 72 while (board[humanMove] != 0) { //looking for an empty cell. 73 humanMove ++; 74 if (humanMove >8) humanMove = 0; 75 } 76 77 //--------------------------------------------------\\- 78 //this makes the flashing "?" possible. Every 200 milliseconds the IF condition becomes true and it will toogle the 79 //showDot variable between True and False (and reset the timerPos value at the current millis() value. 80 if (timerPos + 200 < millis()) { 81 timerPos = millis(); 82 showDot = !showDot; 83 playhuman_showpos( humanMove, showDot); //calling the function that will draw (or delete) the "?" 84 } 85 //--------------------------------------------------/- 86 87 if (digitalRead(BUTTON_MOVE)==LOW) { //the player hit the MOVE button. 88 playhuman_showpos( humanMove, false); //delete the marker 89 humanMove ++; //move the "?" to the next cell. 90 91 while (digitalRead(BUTTON_MOVE)==LOW); //debounce 92 93 bool showDot = false; //this two lines make sure the "?" is displayed 94 long timerPos =-1000; //at the first round. 95 } 96 97 if (digitalRead(BUTTON_OK )==LOW) stayInLoop = false; //the player hit the OK button and made his/her choice. 98 99 delay(100); //required for a correct display. 100 } 101 102 board[humanMove] = 1; //let's assign the chosen cell to the player. 103 104 105} 106 107//------------------------------------------------------------------ 108 109void playhuman_showpos(int humanMove, bool showDot) { //this function draw a flashing "?" (white = draw, black = delete) 110 111 display.setTextSize(2); 112 if (humanMove == 0) display.setCursor( 5, 5); 113 else if (humanMove == 1) display.setCursor(25, 5); 114 else if (humanMove == 2) display.setCursor(45, 5); 115 else if (humanMove == 3) display.setCursor( 5,25); 116 else if (humanMove == 4) display.setCursor(25,25); 117 else if (humanMove == 5) display.setCursor(45,25); 118 else if (humanMove == 6) display.setCursor( 5,45); 119 else if (humanMove == 7) display.setCursor(25,45); 120 else if (humanMove == 8) display.setCursor(45,45); 121 122 //if (showDot) {display.setTextColor(WHITE);display.print("?");} else {display.setTextColor(BLACK);display.print("?");} 123 if (showDot) display.setTextColor(WHITE); else display.setTextColor(BLACK); 124 125 display.print("?"); 126 display.display(); 127} 128 129 130//-------------------------------------------------------------------------------------------------------- 131void playcpu() { 132 133 //NOTE: The player has almost no chance to win, since the cpu will check every possible move. 134 // Actually the only way to beat the cpu is to have two winning move at the same time. 135 136 //The CPU has no real strategy, actually; it just prevents the player to win and put an "X" if it has 137 //a possible winning move. If no winning move are possible, it just put an "X" into a random place. 138 //It could seems a stupid AI, however you will see the CPU will play rather well and it will be 139 //hard to beat it. 140 141 int cpumove = checkboard(2); //2 = cpu let's check if there's a cpu's winner move 142 143 if (cpumove >=0) { 144 board[cpumove] = 2; //cpu's winner move 145 } 146 else { 147 cpumove = checkboard(1); //1=player check if the player has a chance to win (2 circles and an empty cell in a row) 148 if (cpumove >=0) { 149 board[cpumove] = 2; //this move will break the player's winner move 150 } 151 152 //there's no possible winner move neither for the cpu, nor for the human;: the CPU will put an "X" in a random cell 153 while (cpumove < 0) { //looking for a random, empty cell. 154 int randomMove = random(10); 155 if (randomMove >=0 && randomMove <=8 && board[randomMove] == 0) { 156 cpumove = randomMove; 157 } 158 } 159 board[cpumove] = 2; //let's assign the empty cell to the CPU 160 } 161} 162 163 164//-------------------------------------------------------------------------------------------------------- 165int checkboard(int x){ //x = 1 -> player, x = 2 -> cpu 166 167 //this function checks if the next move can be the winning move and return the cell that will 168 //win the game. It's used by the CPU to decide if it can win or if the player is going to win 169 //(and placing an "X" to prevent this chance). 170 //if no move wins the game, it returns -1 171 //the board[] index is 0 1 2 172 // 3 4 5 173 // 6 7 8 174 175 176 if (board[0]==0 && board[1]==x && board[2]==x) return 0; // 0 1 1 177 // . . . 178 // . . . 179 180 else if (board[0]==x && board[1]==0 && board[2]==x) return 1; // 1 0 1 181 // . . . 182 // . . . 183 184 else if (board[0]==x && board[1]==x && board[2]==0) return 2; // 1 1 0 185 // . . . 186 // . . . 187 //------------------------------------------------- 188 else if (board[3]==0 && board[4]==x && board[5]==x) return 3; // . . . 189 // 0 1 1 190 // . . . 191 192 else if (board[3]==x && board[4]==0 && board[5]==x) return 4; // . . . 193 // 1 0 1 194 // . . . 195 196 else if (board[3]==x && board[4]==x && board[5]==0) return 5; // . . . 197 // 1 1 0 198 // . . . 199 //------------------------------------------------- 200 else if (board[6]==0 && board[7]==x && board[8]==x) return 6; // . . . 201 // . . . 202 // 0 1 1 203 204 else if (board[6]==x && board[7]==0 && board[8]==x) return 7; // . . . 205 // . . . 206 // 1 0 1 207 208 else if (board[6]==x && board[7]==x && board[8]==0) return 8; // . . . 209 // . . . 210 // 1 1 0 211 212 //------------------------------------------------- 213 else if (board[0]==0 && board[3]==x && board[6]==x) return 0; // 0 . . 214 // 1 . . 215 // 1 . . 216 217 else if (board[0]==x && board[3]==0 && board[6]==x) return 3; // 1 . . 218 // 0 . . 219 // 1 . . 220 221 else if (board[0]==x && board[3]==x && board[6]==0) return 6; // 1 . . 222 // 1 . . 223 // 0 . . 224 225 //------------------------------------------------- 226 else if (board[1]==0 && board[4]==x && board[7]==x) return 1; // . 0 . 227 // . 1 . 228 // . 1 . 229 230 else if (board[1]==x && board[4]==0 && board[7]==x) return 4; // . 1 . 231 // . 0 . 232 // . 1 . 233 234 else if (board[1]==x && board[4]==x && board[7]==0) return 7; // . 1 . 235 // . 1 . 236 // . 0 . 237 238 //------------------------------------------------- 239 else if (board[2]==0 && board[5]==x && board[8]==x) return 2; // . . 0 240 // . . 1 241 // . . 1 242 243 else if (board[2]==x && board[5]==0 && board[8]==x) return 5; // . . 1 244 // . . 0 245 // . . 1 246 247 else if (board[2]==x && board[5]==x && board[8]==0) return 8; // . . 1 248 // . . 1 249 // . . 0 250 251 //------------------------------------------------- 252 else if (board[0]==0 && board[4]==x && board[8]==x) return 0; // 0 . . 253 // . 1 . 254 // . . 1 255 256 else if (board[0]==x && board[4]==0 && board[8]==x) return 4; // 1 . . 257 // . 0 . 258 // . . 1 259 260 else if (board[0]==x && board[4]==x && board[8]==0) return 8; // 1 . . 261 // . 1 . 262 // . . 0 263 264 //------------------------------------------------- 265 else if (board[2]==0 && board[4]==x && board[6]==x) return 2; // . . 0 266 // . 1 . 267 // 1 . . 268 269 else if (board[2]==x && board[4]==0 && board[6]==x) return 4; // . . 1 270 // . 0 . 271 // 1 . . 272 273 else if (board[2]==x && board[4]==x && board[6]==0) return 6; // . . 1 274 // . 1 . 275 // 0 . . 276 277 else return -1; 278} 279 280 281//-------------------------------------------------------------------------------------------- 282void checkWinner() { //check the board to see if there is a winner 283 284 winner = 3; //3=draft, 1= winner->player, 2=winner->cpu 285 286 // circles win? 287 if (board[0]==1 && board[1]==1 && board[2]==1) winner=1; 288 else if (board[3]==1 && board[4]==1 && board[5]==1) winner=1; 289 else if (board[6]==1 && board[7]==1 && board[8]==1) winner=1; 290 else if (board[0]==1 && board[3]==1 && board[6]==1) winner=1; 291 else if (board[1]==1 && board[4]==1 && board[7]==1) winner=1; 292 else if (board[2]==1 && board[5]==1 && board[8]==1) winner=1; 293 else if (board[0]==1 && board[4]==1 && board[8]==1) winner=1; 294 else if (board[2]==1 && board[4]==1 && board[6]==1) winner=1; 295 296 // crosses win? 297 else if (board[0]==2 && board[1]==2 && board[2]==2) winner=2; 298 else if (board[3]==2 && board[4]==2 && board[5]==2) winner=2; 299 else if (board[6]==2 && board[7]==2 && board[8]==2) winner=2; 300 else if (board[0]==2 && board[3]==2 && board[6]==2) winner=2; 301 else if (board[1]==2 && board[4]==2 && board[7]==2) winner=2; 302 else if (board[2]==2 && board[5]==2 && board[8]==2) winner=2; 303 else if (board[0]==2 && board[4]==2 && board[8]==2) winner=2; 304 else if (board[2]==2 && board[4]==2 && board[6]==2) winner=2; 305 306 if (winner == 3) { 307 for(int i=0;i<9;i++) if (board[i]==0) winner=0; //there are some empty cells yet. 308 } 309 310 311} 312 313//-------------------------------------------------------------------------------------------------------------- 314 315void resetGame() { 316 317 for(int i=0;i<9;i++) board[i]=0; //Resetting the board. 0 = empty cell, 1 = player circle, 2 = CPU cross 318 319 winner = 0; 320 gameStatus = 0; 321 322} 323 324//-------------------------------------------------------------------------------------------------------------- 325 326void boardDrawing() { 327 328 display.clearDisplay(); 329 display.setTextColor(WHITE); 330 331 display.drawFastHLine(0, 21, 64, WHITE); //horizontal lines 332 display.drawFastHLine(0, 42, 64, WHITE); 333 334 display.drawFastVLine(21, 0, 64, WHITE); //vertical lines 335 display.drawFastVLine(42, 0, 64, WHITE); 336 337 //drawing the content of the nine cells: " ", "o", "x" 338 display.setTextSize(2); 339 display.setCursor( 5, 5); display.print(charBoard(0)); display.setCursor(25, 5); display.print(charBoard(1)); display.setCursor(45, 5); display.print(charBoard(2)); 340 display.setCursor( 5,25); display.print(charBoard(3)); display.setCursor(25,25); display.print(charBoard(4)); display.setCursor(45,25); display.print(charBoard(5)); 341 display.setCursor( 5,45); display.print(charBoard(6)); display.setCursor(25,45); display.print(charBoard(7)); display.setCursor(45,45); display.print(charBoard(8)); 342 display.display(); 343 344 delay(200); //DON'T REMOVE!!!! needed for correct refresh and further flashing "?" when it's the player turn!!! 345} 346 347//-------------------------------------------------------------------------------------------------------------- 348String charBoard(int x) { 349 if (board[x] == 0) return " "; 350 if (board[x] == 1) return "o"; 351 if (board[x] == 2) return "x"; 352 353 return "?"; //error trap; but it's impossible it can return an "?" because the board[] array is all initialized = 0 354} 355 356//-------------------------------------------------------------------------------------------------------------- 357void setup() { //function executed once, at every boot or reset. 358 359 randomSeed(analogRead(0)); //resetting the random function behavior. 360 361 //using the internal pullup resistor, so the button will be LOW until a button is pressed. 362 pinMode(BUTTON_MOVE,INPUT_PULLUP); //Declaring the pin #2 as input (connected to the button move) 363 pinMode(BUTTON_OK ,INPUT_PULLUP); //Declaring the pin #3 as input (connected to the button ok) 364 365//display.begin(); //if the display doesn't work with the beHIGH instruction, 366//display.begin(SSD1306_SWITCHCAPVCC, 0x3D); //try one of these two ones. 367 display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 368 369 delay(500); //needed for display correct initializing 370 display.clearDisplay(); //clearing the display 371 display.setTextColor(WHITE); //setting the display color 372 display.display(); //executing the above instructions. The SSD1306 dislay will not execute any command until to use the ::display() command. 373 374 whosplaying = 2; //deciding who's the first player. Set = 2 to force it entering in the folHIGHing while loop. 375 while ( whosplaying <0 || whosplaying > 1) whosplaying = random(2); //it will stay in the loop until whosplaying isn't = 0 or = 1. Probably there's no 376 //need for a loop, since random(2) should return 0 or 1, but I'm an old programmer, 377 //I've seen many strange things in my programmer life and I like to be sure ;-) 378} 379 380 381//-------------------------------------------------------------------------------------------------------------- 382void loop() { //main loop. Endlessly executed by Arduino. 383 384 if (gameStatus == 0){ //this is where I always put a menu in the Arduino games I wrote. We don't need a menu here, so it's just a reset step. 385 resetGame(); 386 boardDrawing(); //drawing an empty board 387 gameStatus = 1; //starting the game (see beHIGH) 388 winner = 0; //no winner for now (winner = 1: player, winner = 2: cpu). 389 } 390 391 //--------------------------------------------- 392 393 if (gameStatus == 1){ //starting the game 394 395 while (winner == 0) { //game main loop: loop until no one wins the match. 396 397 display.setTextSize(2); 398 399 if (whosplaying == 0) { //whosplaying = 0: cpu turn 400 401 display.setCursor( 72,25); display.print("CPU"); 402 display.display(); 403 delay(1000); 404 405 playcpu(); //in this function the CPU play its move. 406 407 whosplaying =1; //changing the turn. 408 } 409 else { 410 411 display.setCursor( 72,25); display.print("You"); 412 display.display(); 413 414 playhuman(); //in this function the player makes his/her move. 415 416 whosplaying =0; //changing the turn. 417 } 418 419 boardDrawing(); //refreshing the board with all the moves already done. 420 delay(500); 421 422 checkWinner(); //this will check if there's a winner and assign the winner variable 423 424 if (winner > 0) { 425 426 427 if (winner == 3) { 428 display.setTextSize(2); display.setCursor( 68, 25); 429 display.print("Draft"); 430 } 431 else { 432 //showing who's the winner 433 display.setTextSize(2); display.setCursor( 72, 25); 434 if (winner == 1) { Serial.println(F("You")); display.print("You"); display.setCursor( 72, 45); display.print("win"); } 435 else { Serial.println(F("CPU")); display.print("CPU"); display.setCursor( 72, 45); display.print("wins");} 436 437 } 438 display.display(); 439 delay(1000); 440 441 //debounce loop. It will not proceed until both buttons are unpressed. 442 while (digitalRead(BUTTON_MOVE)==HIGH && digitalRead(BUTTON_OK )==HIGH); 443 444 } 445 446 //display.display(); 447 448 } 449 450 //swap the first move for the next match between CPU and human (one per match) 451 if (whosplaying == 0) whosplaying =1; else whosplaying =0; 452 453 gameStatus = 0; //entering the reset step 454 delay(1000); //just wait a second 455 456 } 457 458} 459 460 461 462
Display Source Code (.h)
arduino
download and add these two files to the sketch folder if you display doesn't work.
1/********************************************************************* 2This is a library for our Monochrome OLEDs based on SSD1306 drivers 3 4 MODIFIED by Giovanni Verrua, SEE BELOW 5 6 Pick one up today in the adafruit shop! 7 ------> http://www.adafruit.com/category/63_98 8 9These displays use SPI to communicate, 4 or 5 pins are required to 10interface 11 12Adafruit invests time and resources providing this open source code, 13please support Adafruit and open-source hardware by purchasing 14products from Adafruit! 15 16Written by Limor Fried/Ladyada for Adafruit Industries. 17BSD license, check license.txt for more information 18All text above, and the splash screen must be included in any redistribution 19*********************************************************************/ 20#ifndef _Adafruit_SSD1306_H_ 21#define _Adafruit_SSD1306_H_ 22 23#if ARDUINO >= 100 24 #include "Arduino.h" 25 #define WIRE_WRITE Wire.write 26#else 27 #include "WProgram.h" 28 #define WIRE_WRITE Wire.send 29#endif 30 31#if defined(__SAM3X8E__) 32 typedef volatile RwReg PortReg; 33 typedef uint32_t PortMask; 34 #define HAVE_PORTREG 35#elif defined(ARDUINO_ARCH_SAMD) 36// not supported 37#elif defined(ESP8266) || defined(ESP32) || defined(ARDUINO_STM32_FEATHER) || defined(__arc__) 38 typedef volatile uint32_t PortReg; 39 typedef uint32_t PortMask; 40#elif defined(__AVR__) 41 typedef volatile uint8_t PortReg; 42 typedef uint8_t PortMask; 43 #define HAVE_PORTREG 44#else 45 // chances are its 32 bit so assume that 46 typedef volatile uint32_t PortReg; 47 typedef uint32_t PortMask; 48#endif 49 50#include <SPI.h> 51#include <Adafruit_GFX.h> 52 53#define BLACK 0 54#define WHITE 1 55#define INVERSE 2 56 57 58/*========================================================================= 59 SSD1306 Displays 60 ----------------------------------------------------------------------- 61 The driver is used in multiple displays (128x64, 128x32, etc.). 62 Select the appropriate display below to create an appropriately 63 sized framebuffer, etc. 64 65 SSD1306_128_64 128x64 pixel display 66 67 SSD1306_128_32 128x32 pixel display 68 69 SSD1306_96_16 70 71 -----------------------------------------------------------------------*/ 72 73//------------------------------------------------------------------------------------| 74//modified by Giovanni Verrua. Change it if the display doesn't work 75#define SSD1306_I2C_ADDRESS 0x3C // 011110+SA0+RW - 0x3C or 0x3D 76// Address for 128x32 is 0x3C 77// Address for 128x64 is 0x3D (default) or 0x3C (if SA0 is grounded) 78 79 #define SSD1306_128_64 80// #define SSD1306_128_32 81// #define SSD1306_96_16 82//------------------------------------------------------------------------------------| 83 84 85 86/*=========================================================================*/ 87 88#if defined SSD1306_128_64 && defined SSD1306_128_32 89 #error "Only one SSD1306 display can be specified at once in SSD1306.h" 90#endif 91#if !defined SSD1306_128_64 && !defined SSD1306_128_32 && !defined SSD1306_96_16 92 #error "At least one SSD1306 display must be specified in SSD1306.h" 93#endif 94 95#if defined SSD1306_128_64 96 #define SSD1306_LCDWIDTH 128 97 #define SSD1306_LCDHEIGHT 64 98#endif 99#if defined SSD1306_128_32 100 #define SSD1306_LCDWIDTH 128 101 #define SSD1306_LCDHEIGHT 32 102#endif 103#if defined SSD1306_96_16 104 #define SSD1306_LCDWIDTH 96 105 #define SSD1306_LCDHEIGHT 16 106#endif 107 108#define SSD1306_SETCONTRAST 0x81 109#define SSD1306_DISPLAYALLON_RESUME 0xA4 110#define SSD1306_DISPLAYALLON 0xA5 111#define SSD1306_NORMALDISPLAY 0xA6 112#define SSD1306_INVERTDISPLAY 0xA7 113#define SSD1306_DISPLAYOFF 0xAE 114#define SSD1306_DISPLAYON 0xAF 115 116#define SSD1306_SETDISPLAYOFFSET 0xD3 117#define SSD1306_SETCOMPINS 0xDA 118 119#define SSD1306_SETVCOMDETECT 0xDB 120 121#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 122#define SSD1306_SETPRECHARGE 0xD9 123 124#define SSD1306_SETMULTIPLEX 0xA8 125 126#define SSD1306_SETLOWCOLUMN 0x00 127#define SSD1306_SETHIGHCOLUMN 0x10 128 129#define SSD1306_SETSTARTLINE 0x40 130 131#define SSD1306_MEMORYMODE 0x20 132#define SSD1306_COLUMNADDR 0x21 133#define SSD1306_PAGEADDR 0x22 134 135#define SSD1306_COMSCANINC 0xC0 136#define SSD1306_COMSCANDEC 0xC8 137 138#define SSD1306_SEGREMAP 0xA0 139 140#define SSD1306_CHARGEPUMP 0x8D 141 142#define SSD1306_EXTERNALVCC 0x1 143#define SSD1306_SWITCHCAPVCC 0x2 144 145// Scrolling #defines 146#define SSD1306_ACTIVATE_SCROLL 0x2F 147#define SSD1306_DEACTIVATE_SCROLL 0x2E 148#define SSD1306_SET_VERTICAL_SCROLL_AREA 0xA3 149#define SSD1306_RIGHT_HORIZONTAL_SCROLL 0x26 150#define SSD1306_LEFT_HORIZONTAL_SCROLL 0x27 151#define SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL 0x29 152#define SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL 0x2A 153 154class Adafruit_SSD1306 : public Adafruit_GFX { 155 public: 156 Adafruit_SSD1306(int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS); 157 Adafruit_SSD1306(int8_t DC, int8_t RST, int8_t CS); 158 Adafruit_SSD1306(int8_t RST = -1); 159 160 void begin(uint8_t switchvcc = SSD1306_SWITCHCAPVCC, uint8_t i2caddr = SSD1306_I2C_ADDRESS, bool reset=true); 161 void ssd1306_command(uint8_t c); 162 163 void clearDisplay(void); 164 void invertDisplay(uint8_t i); 165 void display(); 166 167 void startscrollright(uint8_t start, uint8_t stop); 168 void startscrollleft(uint8_t start, uint8_t stop); 169 170 void startscrolldiagright(uint8_t start, uint8_t stop); 171 void startscrolldiagleft(uint8_t start, uint8_t stop); 172 void stopscroll(void); 173 174 void dim(boolean dim); 175 176 void drawPixel(int16_t x, int16_t y, uint16_t color); 177 178 virtual void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color); 179 virtual void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color); 180 181 private: 182 int8_t _i2caddr, _vccstate, sid, sclk, dc, rst, cs; 183 void fastSPIwrite(uint8_t c); 184 185 boolean hwSPI; 186#ifdef HAVE_PORTREG 187 PortReg *mosiport, *clkport, *csport, *dcport; 188 PortMask mosipinmask, clkpinmask, cspinmask, dcpinmask; 189#endif 190 191 inline void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, uint16_t color) __attribute__((always_inline)); 192 inline void drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) __attribute__((always_inline)); 193 194}; 195 196#endif /* _Adafruit_SSD1306_H_ */ 197
TicTacToe - OLED version
arduino
This is the sketch for the OLED display version.
1/************************************************************************** 2 * 3 * TicTacToe - Oled 4 * 5 * It requires an Arduino Nano, Uno, Mini Pro 6 * 7 * Hardware connections: 8 * DISPLAY - Arduino Nano/Uno 9 * GND - GND 10 * VDD - 3.3V depending on your model 11 * SCL - A5 12 * SDA - A4 13 * 14 * Arduino pin 2 <-- button MOVE <--+----- +3.3V or +5V 15 * Arduino pin 3 <-- button OK <--+ 16 * 17 * Note: connect two 10...100 kohm pulldown resistors: pin 2 -> GND; pin 3 -> GND 18 * 19 * Install the Adafruit SSD1306 libraries 20 * by Arduino IDE, menu Tools -> Manage Libraries 21 * 22 * apr/2022, Giovanni Verrua 23**************************************************************************/ 24 25//including the needed libraries for the OLED display 26#include <SPI.h> 27#include <Wire.h> 28#include <Adafruit_GFX.h> 29#include <Adafruit_SSD1306.h> 30 31#define BUTTON_MOVE 2 32#define BUTTON_OK 3 33 34// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) 35#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) 36Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET); 37 38 39int gameStatus = 0; 40int whosplaying = 0; //0 = Arduino, 1 = Human 41 42int winner = -1; //-1 = Playing, 0 = Draw, 1 = Human, 2 = CPU 43 44 45int board[]={0,0,0, 46 0,0,0, 47 0,0,0}; //0 = blank, 1 = human (circle), 2 = computer (cross) 48 49 50 51 52 53 54//-------------------------------------------------------------------------------------------------------- 55void playhuman() { 56 57 int humanMove = 0; 58 59 bool stayInLoop = true; 60 bool showDot = false; 61 long timerPos = millis()-1000; 62 63 64 while (stayInLoop) { //stay in loop until the player makes his/her choice hitting the OK button. 65 66 //If the current "?" position isn't avaliable (the cell value is 1 or 2), this loop will 67 //move the "?" to the next free cell. 68 //NOTE: there must be at least one empty cell (or it will never exit from this loop [deadlock]). 69 //This is granted, because if all the cells are used, there's a winner or it's a draft. 70 //(the calling function [loop function] check it before to continue). 71 72 while (board[humanMove] != 0) { //looking for an empty cell. 73 humanMove ++; 74 if (humanMove >8) humanMove = 0; 75 } 76 77 //--------------------------------------------------\\- 78 //this makes the flashing "?" possible. Every 200 milliseconds the IF condition becomes true and it will toogle the 79 //showDot variable between True and False (and reset the timerPos value at the current millis() value. 80 if (timerPos + 200 < millis()) { 81 timerPos = millis(); 82 showDot = !showDot; 83 playhuman_showpos( humanMove, showDot); //calling the function that will draw (or delete) the "?" 84 } 85 //--------------------------------------------------/- 86 87 if (digitalRead(BUTTON_MOVE)==HIGH) { //the player hit the MOVE button. 88 playhuman_showpos( humanMove, false); //delete the marker 89 humanMove ++; //move the "?" to the next cell. 90 91 while (digitalRead(BUTTON_MOVE)==HIGH); //debounce 92 93 bool showDot = false; //this two lines make sure the "?" is displayed 94 long timerPos =-1000; //at the first round. 95 } 96 97 if (digitalRead(BUTTON_OK )==HIGH) stayInLoop = false; //the player hit the OK button and made his/her choice. 98 99 delay(100); //required for a correct display. 100 } 101 102 board[humanMove] = 1; //let's assign the chosen cell to the player. 103 104 105} 106 107//------------------------------------------------------------------ 108 109void playhuman_showpos(int humanMove, bool showDot) { //this function draw a flashing "?" (white = draw, black = delete) 110 111 display.setTextSize(2); 112 if (humanMove == 0) display.setCursor( 5, 5); 113 else if (humanMove == 1) display.setCursor(25, 5); 114 else if (humanMove == 2) display.setCursor(45, 5); 115 else if (humanMove == 3) display.setCursor( 5,25); 116 else if (humanMove == 4) display.setCursor(25,25); 117 else if (humanMove == 5) display.setCursor(45,25); 118 else if (humanMove == 6) display.setCursor( 5,45); 119 else if (humanMove == 7) display.setCursor(25,45); 120 else if (humanMove == 8) display.setCursor(45,45); 121 122 //if (showDot) {display.setTextColor(WHITE);display.print("?");} else {display.setTextColor(BLACK);display.print("?");} 123 if (showDot) display.setTextColor(WHITE); else display.setTextColor(BLACK); 124 125 display.print("?"); 126 display.display(); 127} 128 129 130//-------------------------------------------------------------------------------------------------------- 131void playcpu() { 132 133 //NOTE: The player has almost no chance to win, since the cpu will check every possible move. 134 // Actually the only way to beat the cpu is to have two winning move at the same time. 135 136 //The CPU has no real strategy, actually; it just prevents the player to win and put an "X" if it has 137 //a possible winning move. If no winning move are possible, it just put an "X" into a random place. 138 //It could seems a stupid AI, however you will see the CPU will play rather well and it will be 139 //hard to beat it. 140 141 int cpumove = checkboard(2); //2 = cpu let's check if there's a cpu's winner move 142 143 if (cpumove >=0) { 144 board[cpumove] = 2; //cpu's winner move 145 } 146 else { 147 cpumove = checkboard(1); //1=player check if the player has a chance to win (2 circles and an empty cell in a row) 148 if (cpumove >=0) { 149 board[cpumove] = 2; //this move will break the player's winner move 150 } 151 152 //there's no possible winner move neither for the cpu, nor for the human;: the CPU will put an "X" in a random cell 153 while (cpumove < 0) { //looking for a random, empty cell. 154 int randomMove = random(10); 155 if (randomMove >=0 && randomMove <=8 && board[randomMove] == 0) { 156 cpumove = randomMove; 157 } 158 } 159 board[cpumove] = 2; //let's assign the empty cell to the CPU 160 } 161} 162 163 164//-------------------------------------------------------------------------------------------------------- 165int checkboard(int x){ //x = 1 -> player, x = 2 -> cpu 166 167 //this function checks if the next move can be the winning move and return the cell that will 168 //win the game. It's used by the CPU to decide if it can win or if the player is going to win 169 //(and placing an "X" to prevent this chance). 170 //if no move wins the game, it returns -1 171 //the board[] index is 0 1 2 172 // 3 4 5 173 // 6 7 8 174 175 176 if (board[0]==0 && board[1]==x && board[2]==x) return 0; // 0 1 1 177 // . . . 178 // . . . 179 180 else if (board[0]==x && board[1]==0 && board[2]==x) return 1; // 1 0 1 181 // . . . 182 // . . . 183 184 else if (board[0]==x && board[1]==x && board[2]==0) return 2; // 1 1 0 185 // . . . 186 // . . . 187 //------------------------------------------------- 188 else if (board[3]==0 && board[4]==x && board[5]==x) return 3; // . . . 189 // 0 1 1 190 // . . . 191 192 else if (board[3]==x && board[4]==0 && board[5]==x) return 4; // . . . 193 // 1 0 1 194 // . . . 195 196 else if (board[3]==x && board[4]==x && board[5]==0) return 5; // . . . 197 // 1 1 0 198 // . . . 199 //------------------------------------------------- 200 else if (board[6]==0 && board[7]==x && board[8]==x) return 6; // . . . 201 // . . . 202 // 0 1 1 203 204 else if (board[6]==x && board[7]==0 && board[8]==x) return 7; // . . . 205 // . . . 206 // 1 0 1 207 208 else if (board[6]==x && board[7]==x && board[8]==0) return 8; // . . . 209 // . . . 210 // 1 1 0 211 212 //------------------------------------------------- 213 else if (board[0]==0 && board[3]==x && board[6]==x) return 0; // 0 . . 214 // 1 . . 215 // 1 . . 216 217 else if (board[0]==x && board[3]==0 && board[6]==x) return 3; // 1 . . 218 // 0 . . 219 // 1 . . 220 221 else if (board[0]==x && board[3]==x && board[6]==0) return 6; // 1 . . 222 // 1 . . 223 // 0 . . 224 225 //------------------------------------------------- 226 else if (board[1]==0 && board[4]==x && board[7]==x) return 1; // . 0 . 227 // . 1 . 228 // . 1 . 229 230 else if (board[1]==x && board[4]==0 && board[7]==x) return 4; // . 1 . 231 // . 0 . 232 // . 1 . 233 234 else if (board[1]==x && board[4]==x && board[7]==0) return 7; // . 1 . 235 // . 1 . 236 // . 0 . 237 238 //------------------------------------------------- 239 else if (board[2]==0 && board[5]==x && board[8]==x) return 2; // . . 0 240 // . . 1 241 // . . 1 242 243 else if (board[2]==x && board[5]==0 && board[8]==x) return 5; // . . 1 244 // . . 0 245 // . . 1 246 247 else if (board[2]==x && board[5]==x && board[8]==0) return 8; // . . 1 248 // . . 1 249 // . . 0 250 251 //------------------------------------------------- 252 else if (board[0]==0 && board[4]==x && board[8]==x) return 0; // 0 . . 253 // . 1 . 254 // . . 1 255 256 else if (board[0]==x && board[4]==0 && board[8]==x) return 4; // 1 . . 257 // . 0 . 258 // . . 1 259 260 else if (board[0]==x && board[4]==x && board[8]==0) return 8; // 1 . . 261 // . 1 . 262 // . . 0 263 264 //------------------------------------------------- 265 else if (board[2]==0 && board[4]==x && board[6]==x) return 2; // . . 0 266 // . 1 . 267 // 1 . . 268 269 else if (board[2]==x && board[4]==0 && board[6]==x) return 4; // . . 1 270 // . 0 . 271 // 1 . . 272 273 else if (board[2]==x && board[4]==x && board[6]==0) return 6; // . . 1 274 // . 1 . 275 // 0 . . 276 277 else return -1; 278} 279 280 281//-------------------------------------------------------------------------------------------- 282void checkWinner() { //check the board to see if there is a winner 283 284 winner = 3; //3=draft, 1= winner->player, 2=winner->cpu 285 286 // circles win? 287 if (board[0]==1 && board[1]==1 && board[2]==1) winner=1; 288 else if (board[3]==1 && board[4]==1 && board[5]==1) winner=1; 289 else if (board[6]==1 && board[7]==1 && board[8]==1) winner=1; 290 else if (board[0]==1 && board[3]==1 && board[6]==1) winner=1; 291 else if (board[1]==1 && board[4]==1 && board[7]==1) winner=1; 292 else if (board[2]==1 && board[5]==1 && board[8]==1) winner=1; 293 else if (board[0]==1 && board[4]==1 && board[8]==1) winner=1; 294 else if (board[2]==1 && board[4]==1 && board[6]==1) winner=1; 295 296 // crosses win? 297 else if (board[0]==2 && board[1]==2 && board[2]==2) winner=2; 298 else if (board[3]==2 && board[4]==2 && board[5]==2) winner=2; 299 else if (board[6]==2 && board[7]==2 && board[8]==2) winner=2; 300 else if (board[0]==2 && board[3]==2 && board[6]==2) winner=2; 301 else if (board[1]==2 && board[4]==2 && board[7]==2) winner=2; 302 else if (board[2]==2 && board[5]==2 && board[8]==2) winner=2; 303 else if (board[0]==2 && board[4]==2 && board[8]==2) winner=2; 304 else if (board[2]==2 && board[4]==2 && board[6]==2) winner=2; 305 306 if (winner == 3) { 307 for(int i=0;i<9;i++) if (board[i]==0) winner=0; //there are some empty cells yet. 308 } 309 310 311} 312 313//-------------------------------------------------------------------------------------------------------------- 314 315void resetGame() { 316 317 for(int i=0;i<9;i++) board[i]=0; //Resetting the board. 0 = empty cell, 1 = player circle, 2 = CPU cross 318 319 winner = 0; 320 gameStatus = 0; 321 322} 323 324//-------------------------------------------------------------------------------------------------------------- 325 326void boardDrawing() { 327 328 display.clearDisplay(); 329 display.setTextColor(WHITE); 330 331 display.drawFastHLine(0, 21, 64, WHITE); //horizontal lines 332 display.drawFastHLine(0, 42, 64, WHITE); 333 334 display.drawFastVLine(21, 0, 64, WHITE); //vertical lines 335 display.drawFastVLine(42, 0, 64, WHITE); 336 337 //drawing the content of the nine cells: " ", "o", "x" 338 display.setTextSize(2); 339 display.setCursor( 5, 5); display.print(charBoard(0)); display.setCursor(25, 5); display.print(charBoard(1)); display.setCursor(45, 5); display.print(charBoard(2)); 340 display.setCursor( 5,25); display.print(charBoard(3)); display.setCursor(25,25); display.print(charBoard(4)); display.setCursor(45,25); display.print(charBoard(5)); 341 display.setCursor( 5,45); display.print(charBoard(6)); display.setCursor(25,45); display.print(charBoard(7)); display.setCursor(45,45); display.print(charBoard(8)); 342 display.display(); 343 344 delay(200); //DON'T REMOVE!!!! needed for correct refresh and further flashing "?" when it's the player turn!!! 345} 346 347//-------------------------------------------------------------------------------------------------------------- 348String charBoard(int x) { 349 if (board[x] == 0) return " "; 350 if (board[x] == 1) return "o"; 351 if (board[x] == 2) return "x"; 352 353 return "?"; //error trap; but it's impossible it can return an "?" because the board[] array is all initialized = 0 354} 355 356//-------------------------------------------------------------------------------------------------------------- 357void setup() { //function executed once, at every boot or reset. 358 359 randomSeed(analogRead(0)); //resetting the random function behavior. 360 361 pinMode(BUTTON_MOVE,INPUT); //Declaring the pin #2 as input (connected to the button move) 362 pinMode(BUTTON_OK ,INPUT); //Declaring the pin #3 as input (connected to the button ok) 363 364//display.begin(); //if the display doesn't work with the below instruction, 365//display.begin(SSD1306_SWITCHCAPVCC, 0x3D); //try one of these two ones. 366 display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 367 368 delay(500); //needed for display correct initializing 369 display.clearDisplay(); //clearing the display 370 display.setTextColor(WHITE); //setting the display color 371 display.display(); //executing the above instructions. The SSD1306 dislay will not execute any command until to use the ::display() command. 372 373 whosplaying = 2; //deciding who's the first player. Set = 2 to force it entering in the following while loop. 374 while ( whosplaying <0 || whosplaying > 1) whosplaying = random(2); //it will stay in the loop until whosplaying isn't = 0 or = 1. Probably there's no 375 //need for a loop, since random(2) should return 0 or 1, but I'm an old programmer, 376 //I've seen many strange things in my programmer life and I like to be sure ;-) 377} 378 379 380//-------------------------------------------------------------------------------------------------------------- 381void loop() { //main loop. Endlessly executed by Arduino. 382 383 if (gameStatus == 0){ //this is where I always put a menu in the Arduino games I wrote. We don't need a menu here, so it's just a reset step. 384 resetGame(); 385 boardDrawing(); //drawing an empty board 386 gameStatus = 1; //starting the game (see below) 387 winner = 0; //no winner for now (winner = 1: player, winner = 2: cpu). 388 } 389 390 //--------------------------------------------- 391 392 if (gameStatus == 1){ //starting the game 393 394 while (winner == 0) { //game main loop: loop until no one wins the match. 395 396 display.setTextSize(2); 397 398 if (whosplaying == 0) { //whosplaying = 0: cpu turn 399 400 display.setCursor( 72,25); display.print("CPU"); 401 display.display(); 402 delay(1000); 403 404 playcpu(); //in this function the CPU play its move. 405 406 whosplaying =1; //changing the turn. 407 } 408 else { 409 410 display.setCursor( 72,25); display.print("You"); 411 display.display(); 412 413 playhuman(); //in this function the player makes his/her move. 414 415 whosplaying =0; //changing the turn. 416 } 417 418 boardDrawing(); //refreshing the board with all the moves already done. 419 delay(500); 420 421 checkWinner(); //this will check if there's a winner and assign the winner variable 422 423 if (winner > 0) { 424 425 426 if (winner == 3) { 427 display.setTextSize(2); display.setCursor( 68, 25); 428 display.print("Draft"); 429 } 430 else { 431 //showing who's the winner 432 display.setTextSize(2); display.setCursor( 72, 25); 433 if (winner == 1) { Serial.println(F("You")); display.print("You"); display.setCursor( 72, 45); display.print("win"); } 434 else { Serial.println(F("CPU")); display.print("CPU"); display.setCursor( 72, 45); display.print("wins");} 435 436 } 437 display.display(); 438 delay(1000); 439 440 //debounce loop. It will not proceed until both buttons are unpressed. 441 while (digitalRead(BUTTON_MOVE)==LOW && digitalRead(BUTTON_OK )==LOW); 442 443 } 444 445 //display.display(); 446 447 } 448 449 //swap the first move for the next match between CPU and human (one per match) 450 if (whosplaying == 0) whosplaying =1; else whosplaying =0; 451 452 gameStatus = 0; //entering the reset step 453 delay(1000); //just wait a second 454 455 } 456 457} 458 459 460 461
TicTacToe - Serial Monitor version
arduino
This sketch requires the Arduino board only. You can play using the Arduino IDE serial monitor
1/************************************************************************** 2 * 3 * TicTacToe - Serial monitor version. 4 * Note: the OLED version code is better described; if you have any 5 * doubt about any instruction, please take a look to that version. 6 * 7 * It only requires an Arduino (tested with Nano and Uno, but it 8 * should work with (almost?) any Arduino and other clones 9 * 10 * To use it, you need to connect the Arduino to USB and call the 11 * Serial monitor from inside Arduino IDE: Menu tools -> serial monitor 12 * (or pressing Ctrl + Shift + M) 13 * 14 * Move using the 1...9 keys (plus Enter) from inside the Serial Monitor: 15 * 16 * 1 | 2 | 3 17 * ---+---+--- 18 * 4 | 5 | 6 19 * ---+---+--- 20 * 7 | 8 | 9 21 * 22 * Giovanni Verrua, april 2022 23**************************************************************************/ 24 25 26int gameStatus = 0; 27int whosplaying = 0; //0 = Arduino, 1 = Human 28 29int winner = -1; //-1 = Playing, 0 = Draw, 1 = Human, 2 = CPU 30 31 32int board[]={0,0,0, 33 0,0,0, 34 0,0,0}; //0 = blank, 1 = human (circle), 2 = computer (cross) 35 36 37 38 39 40//-------------------------------------------------------------------------------------------------------- 41void playhuman() { 42 43 int humanMove = -1; 44 bool stayInLoop = true; 45 46 while (stayInLoop) { 47 48 Serial.println(F("\ 49Make your move\ 50")); 51 52 while (Serial.available() && Serial.read()); // empty buffer 53 while (!Serial.available()); // wait for data 54 55 if (Serial.available()) { //data available. 56 57 int humanMove = Serial.read() - 48; //serial.read() return the ascii value of the pressed key. The "1" ascii value is 49. 58 59 Serial.println(humanMove); 60 61 if (humanMove >=0 && humanMove <=9) { 62 63 if (board[humanMove-1] !=0) { 64 Serial.println(F("\ 65Error - Cell already in use")); 66 stayInLoop = true; 67 } 68 else { 69 stayInLoop = false; 70 board[humanMove-1] = 1; //remember: the player uses the 1..9 keys, but the array index starts by 0 (9 cells = 0..8) 71 } 72 } 73 else { 74 Serial.println(F("\ 75type error (just 1 to 9)")); 76 stayInLoop = true; 77 } 78 79 } 80 } 81 82 83 84 85} 86 87 88//-------------------------------------------------------------------------------------------------------- 89void playcpu() { 90 91 int cpumove = checkboard(2); //2 = cpu let's check if there's a cpu's winner move 92 93 if (cpumove >=0) { 94 board[cpumove] = 2; //cpu's winner move 95 } 96 else { 97 cpumove = checkboard(1); //1=player check if the player has a chance to win (2 circles and an empty cell in a row) 98 if (cpumove >=0) { 99 board[cpumove] = 2; //this move will break the player's winner move 100 } 101 102 //there's no possible winner move neither for the cpu, nor for the human, I will put an "X" in a random cell 103 while (cpumove < 0) { 104 int randomMove = random(10); 105 if (randomMove >=0 && randomMove <=8 && board[randomMove] == 0) { 106 cpumove = randomMove; 107 } 108 } 109 board[cpumove] = 2; 110 } 111} 112 113 114//-------------------------------------------------------------------------------------------------------- 115int checkboard(int x){ //x = 1 -> player, x = 2 -> cpu 116 117//full case 118 119 if (board[0]==0 && board[1]==x && board[2]==x) return 0; // 0 1 1 120 // . . . 121 // . . . 122 123 else if (board[0]==x && board[1]==0 && board[2]==x) return 1; // 1 0 1 124 // . . . 125 // . . . 126 127 else if (board[0]==x && board[1]==x && board[2]==0) return 2; // 1 1 0 128 // . . . 129 // . . . 130 //------------------------------------------------- 131 else if (board[3]==0 && board[4]==x && board[5]==x) return 3; // . . . 132 // 0 1 1 133 // . . . 134 135 else if (board[3]==x && board[4]==0 && board[5]==x) return 4; // . . . 136 // 1 0 1 137 // . . . 138 139 else if (board[3]==x && board[4]==x && board[5]==0) return 5; // . . . 140 // 1 1 0 141 // . . . 142 //------------------------------------------------- 143 else if (board[6]==0 && board[7]==x && board[8]==x) return 6; // . . . 144 // . . . 145 // 0 1 1 146 147 else if (board[6]==x && board[7]==0 && board[8]==x) return 7; // . . . 148 // . . . 149 // 1 0 1 150 151 else if (board[6]==x && board[7]==x && board[8]==0) return 8; // . . . 152 // . . . 153 // 1 1 0 154 155 //------------------------------------------------- 156 else if (board[0]==0 && board[3]==x && board[6]==x) return 0; // 0 . . 157 // 1 . . 158 // 1 . . 159 160 else if (board[0]==x && board[3]==0 && board[6]==x) return 3; // 1 . . 161 // 0 . . 162 // 1 . . 163 164 else if (board[0]==x && board[3]==x && board[6]==0) return 6; // 1 . . 165 // 1 . . 166 // 0 . . 167 168 //------------------------------------------------- 169 else if (board[1]==0 && board[4]==x && board[7]==x) return 1; // . 0 . 170 // . 1 . 171 // . 1 . 172 173 else if (board[1]==x && board[4]==0 && board[7]==x) return 4; // . 1 . 174 // . 0 . 175 // . 1 . 176 177 else if (board[1]==x && board[4]==x && board[7]==0) return 7; // . 1 . 178 // . 1 . 179 // . 0 . 180 181 //------------------------------------------------- 182 else if (board[2]==0 && board[5]==x && board[8]==x) return 2; // . . 0 183 // . . 1 184 // . . 1 185 186 else if (board[2]==x && board[5]==0 && board[8]==x) return 5; // . . 1 187 // . . 0 188 // . . 1 189 190 else if (board[2]==x && board[5]==x && board[8]==0) return 8; // . . 1 191 // . . 1 192 // . . 0 193 194 //------------------------------------------------- 195 else if (board[0]==0 && board[4]==x && board[8]==x) return 0; // 0 . . 196 // . 1 . 197 // . . 1 198 199 else if (board[0]==x && board[4]==0 && board[8]==x) return 4; // 1 . . 200 // . 0 . 201 // . . 1 202 203 else if (board[0]==x && board[4]==x && board[8]==0) return 8; // 1 . . 204 // . 1 . 205 // . . 0 206 207 //------------------------------------------------- 208 else if (board[2]==0 && board[4]==x && board[6]==x) return 2; // . . 0 209 // . 1 . 210 // 1 . . 211 212 else if (board[2]==x && board[4]==0 && board[6]==x) return 4; // . . 1 213 // . 0 . 214 // 1 . . 215 216 else if (board[2]==x && board[4]==x && board[6]==0) return 6; // . . 1 217 // . 1 . 218 // 0 . . 219 220 else return -1; 221} 222 223 224//-------------------------------------------------------------------------------------------- 225 226 227void checkWinner() { //check the board to see if there is a winner 228 229 winner = 3; //3=draft, 1= winner->player, 2=winner->cpu 230 231 // circles win? 232 if (board[0]==1 && board[1]==1 && board[2]==1) winner=1; 233 else if (board[3]==1 && board[4]==1 && board[5]==1) winner=1; 234 else if (board[6]==1 && board[7]==1 && board[8]==1) winner=1; 235 else if (board[0]==1 && board[3]==1 && board[6]==1) winner=1; 236 else if (board[1]==1 && board[4]==1 && board[7]==1) winner=1; 237 else if (board[2]==1 && board[5]==1 && board[8]==1) winner=1; 238 else if (board[0]==1 && board[4]==1 && board[8]==1) winner=1; 239 else if (board[2]==1 && board[4]==1 && board[6]==1) winner=1; 240 241 // crosses win? 242 else if (board[0]==2 && board[1]==2 && board[2]==2) winner=2; 243 else if (board[3]==2 && board[4]==2 && board[5]==2) winner=2; 244 else if (board[6]==2 && board[7]==2 && board[8]==2) winner=2; 245 else if (board[0]==2 && board[3]==2 && board[6]==2) winner=2; 246 else if (board[1]==2 && board[4]==2 && board[7]==2) winner=2; 247 else if (board[2]==2 && board[5]==2 && board[8]==2) winner=2; 248 else if (board[0]==2 && board[4]==2 && board[8]==2) winner=2; 249 else if (board[2]==2 && board[4]==2 && board[6]==2) winner=2; 250 251 if (winner == 3) { 252 for(int i=0;i<9;i++) if (board[i]==0) winner=0; //there are some empty cell yet. 253 } 254 255 256} 257 258//-------------------------------------------------------------------------------------------------------------- 259 260void resetGame() { 261 Serial.println("Resetting game..."); 262 for(int i=0;i<9;i++) board[i]=0; 263 winner = 0; 264 gameStatus = 0; 265 266} 267 268//-------------------------------------------------------------------------------------------------------------- 269 270void boardDrawing() { 271 272 Serial.println(""); 273 Serial.print(F(" ")); Serial.print(charBoard(0)); Serial.print(F(" | ")); Serial.print(charBoard(1)); Serial.print(F(" | ")); Serial.println(charBoard(2)); 274 Serial.println(F(" ---+---+---")); 275 Serial.print(F(" ")); Serial.print(charBoard(3)); Serial.print(F(" | ")); Serial.print(charBoard(4)); Serial.print(F(" | ")); Serial.println(charBoard(5)); 276 Serial.println(F(" ---+---+---")); 277 Serial.print(F(" ")); Serial.print(charBoard(6)); Serial.print(F(" | ")); Serial.print(charBoard(7)); Serial.print(F(" | ")); Serial.println(charBoard(8)); 278 Serial.println(""); 279 280} 281 282 283 284//-------------------------------------------------------------------------------------------------------------- 285String charBoard(int x) { 286 if (board[x] == 0) return " "; 287 if (board[x] == 1) return "o"; 288 if (board[x] == 2) return "x"; 289 290 return "?"; //error trap; it shouldn't pass here. 291} 292 293 294//-------------------------------------------------------------------------------------------------------------- 295void setup() { 296 297 Serial.begin(9600); 298 299 randomSeed(analogRead(0)); //resetting the random function. 300 301} 302 303 304//-------------------------------------------------------------------------------------------------------------- 305void loop() { 306 307 308 if (gameStatus == 0){ 309 Serial.println(F("Let's begin...")); 310 311 whosplaying = 2; 312 while ( whosplaying <0 || whosplaying > 1) { 313 whosplaying = random(2); 314 } 315 316 gameStatus = 1; 317 winner = 0; 318 } 319 320 //--------------------------------------------- 321 322 if (gameStatus == 1){ //start 323 324 if (whosplaying == 1) boardDrawing(); 325 326 327 while (winner == 0) { //game main loop 328 329 if (whosplaying == 0) { 330 Serial.println("CPU turn:"); 331 playcpu(); 332 whosplaying =1; 333 } 334 else { 335 Serial.println("Player turn:"); 336 playhuman(); 337 whosplaying =0; 338 } 339 340 boardDrawing(); 341 checkWinner(); //this will assign the winner variable 342 343 if (winner > 0) { 344 Serial.println(""); 345 if (winner == 3) Serial.print(F("Draft!")); 346 else { 347 Serial.print(F("The winner is ")); 348 if (winner == 1) Serial.println(F("the human")); else Serial.println(F("the CPU")); 349 } 350 351 } 352 353 } 354 resetGame(); 355 Serial.println(""); 356 delay(2000); 357 Serial.println(""); 358 } 359 360} 361 362 363 364
Display Source Code (.cpp)
arduino
download and add these two files to the sketch folder if you display doesn't work.
1/********************************************************************* 2This is a library for our Monochrome OLEDs based on SSD1306 drivers 3 4 Pick one up today in the adafruit shop! 5 ------> http://www.adafruit.com/category/63_98 6 7These displays use SPI to communicate, 4 or 5 pins are required to 8interface 9 10Adafruit invests time and resources providing this open source code, 11please support Adafruit and open-source hardware by purchasing 12products from Adafruit! 13 14Written by Limor Fried/Ladyada for Adafruit Industries. 15BSD license, check license.txt for more information 16All text above, and the splash screen below must be included in any redistribution 17*********************************************************************/ 18 19#ifdef __AVR__ 20 #include <avr/pgmspace.h> 21#elif defined(ESP8266) || defined(ESP32) 22 #include <pgmspace.h> 23#else 24 #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) 25#endif 26 27#if !defined(__ARM_ARCH) && !defined(ENERGIA) && !defined(ESP8266) && !defined(ESP32) && !defined(__arc__) 28 #include <util/delay.h> 29#endif 30 31#include <stdlib.h> 32 33#include <Wire.h> 34#include <SPI.h> 35#include "Adafruit_GFX.h" 36#include "Adafruit_SSD1306.h" 37 38// the memory buffer for the LCD 39 40static uint8_t buffer[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8] = { 410x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 420x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 430x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 440x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 450x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 460x00, 0x80, 0x80, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 470x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 480x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 490x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 500x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00, 510x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 520x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0xFF, 53#if (SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH > 96*16) 540xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 550x80, 0xFF, 0xFF, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 560x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x8C, 0x8E, 0x84, 0x00, 0x00, 0x80, 0xF8, 570xF8, 0xF8, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 580xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xE0, 0xE0, 0xC0, 0x80, 590x00, 0xE0, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 600x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xC7, 0x01, 0x01, 610x01, 0x01, 0x83, 0xFF, 0xFF, 0x00, 0x00, 0x7C, 0xFE, 0xC7, 0x01, 0x01, 0x01, 0x01, 0x83, 0xFF, 620xFF, 0xFF, 0x00, 0x38, 0xFE, 0xC7, 0x83, 0x01, 0x01, 0x01, 0x83, 0xC7, 0xFF, 0xFF, 0x00, 0x00, 630x01, 0xFF, 0xFF, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x07, 0x01, 0x01, 0x01, 0x00, 0x00, 0x7F, 0xFF, 640x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x01, 0xFF, 650xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 660x03, 0x0F, 0x3F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC7, 0xC7, 0x8F, 670x8F, 0x9F, 0xBF, 0xFF, 0xFF, 0xC3, 0xC0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFC, 0xFC, 680xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0xC0, 0x00, 0x01, 0x03, 0x03, 0x03, 690x03, 0x03, 0x01, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 700x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x03, 0x03, 0x01, 0x01, 0x03, 0x03, 0x00, 0x00, 710x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 720x03, 0x03, 0x03, 0x03, 0x03, 0x01, 0x00, 0x00, 0x00, 0x01, 0x03, 0x01, 0x00, 0x00, 0x00, 0x03, 730x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74#if (SSD1306_LCDHEIGHT == 64) 750x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x1F, 0x0F, 760x87, 0xC7, 0xF7, 0xFF, 0xFF, 0x1F, 0x1F, 0x3D, 0xFC, 0xF8, 0xF8, 0xF8, 0xF8, 0x7C, 0x7D, 0xFF, 770xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x0F, 0x07, 0x00, 0x30, 0x30, 0x00, 0x00, 780x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 790x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 800x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xC0, 0x00, 810x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 820x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 830x00, 0xC0, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x1F, 840x0F, 0x07, 0x1F, 0x7F, 0xFF, 0xFF, 0xF8, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0xE0, 850x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 860x00, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x0E, 0xFC, 0xF8, 0x00, 0x00, 0xF0, 0xF8, 0x1C, 0x0E, 870x06, 0x06, 0x06, 0x0C, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFC, 880xFE, 0xFC, 0x00, 0x18, 0x3C, 0x7E, 0x66, 0xE6, 0xCE, 0x84, 0x00, 0x00, 0x06, 0xFF, 0xFF, 0x06, 890x06, 0xFC, 0xFE, 0xFC, 0x0C, 0x06, 0x06, 0x06, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0xC0, 0xF8, 900xFC, 0x4E, 0x46, 0x46, 0x46, 0x4E, 0x7C, 0x78, 0x40, 0x18, 0x3C, 0x76, 0xE6, 0xCE, 0xCC, 0x80, 910x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 920x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x0F, 0x1F, 0x1F, 0x3F, 0x3F, 0x3F, 0x3F, 0x1F, 0x0F, 0x03, 930x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 940x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x03, 0x07, 0x0E, 0x0C, 950x18, 0x18, 0x0C, 0x06, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x01, 0x0F, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 960x07, 0x01, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 970x00, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x07, 980x07, 0x0C, 0x0C, 0x18, 0x1C, 0x0C, 0x06, 0x06, 0x00, 0x04, 0x0E, 0x0C, 0x18, 0x0C, 0x0F, 0x07, 990x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1000x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1010x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1020x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1030x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1040x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1050x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1060x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 107#endif 108#endif 109}; 110 111#define ssd1306_swap(a, b) { int16_t t = a; a = b; b = t; } 112 113// the most basic function, set a single pixel 114void Adafruit_SSD1306::drawPixel(int16_t x, int16_t y, uint16_t color) { 115 if ((x < 0) || (x >= width()) || (y < 0) || (y >= height())) 116 return; 117 118 // check rotation, move pixel around if necessary 119 switch (getRotation()) { 120 case 1: 121 ssd1306_swap(x, y); 122 x = WIDTH - x - 1; 123 break; 124 case 2: 125 x = WIDTH - x - 1; 126 y = HEIGHT - y - 1; 127 break; 128 case 3: 129 ssd1306_swap(x, y); 130 y = HEIGHT - y - 1; 131 break; 132 } 133 134 // x is which column 135 switch (color) 136 { 137 case WHITE: buffer[x+ (y/8)*SSD1306_LCDWIDTH] |= (1 << (y&7)); break; 138 case BLACK: buffer[x+ (y/8)*SSD1306_LCDWIDTH] &= ~(1 << (y&7)); break; 139 case INVERSE: buffer[x+ (y/8)*SSD1306_LCDWIDTH] ^= (1 << (y&7)); break; 140 } 141 142} 143 144Adafruit_SSD1306::Adafruit_SSD1306(int8_t SID, int8_t SCLK, int8_t DC, int8_t RST, int8_t CS) : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) { 145 cs = CS; 146 rst = RST; 147 dc = DC; 148 sclk = SCLK; 149 sid = SID; 150 hwSPI = false; 151} 152 153// constructor for hardware SPI - we indicate DataCommand, ChipSelect, Reset 154Adafruit_SSD1306::Adafruit_SSD1306(int8_t DC, int8_t RST, int8_t CS) : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) { 155 dc = DC; 156 rst = RST; 157 cs = CS; 158 hwSPI = true; 159} 160 161// initializer for I2C - we only indicate the reset pin! 162Adafruit_SSD1306::Adafruit_SSD1306(int8_t reset) : 163Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT) { 164 sclk = dc = cs = sid = -1; 165 rst = reset; 166} 167 168 169void Adafruit_SSD1306::begin(uint8_t vccstate, uint8_t i2caddr, bool reset) { 170 _vccstate = vccstate; 171 _i2caddr = i2caddr; 172 173 // set pin directions 174 if (sid != -1){ 175 pinMode(dc, OUTPUT); 176 pinMode(cs, OUTPUT); 177#ifdef HAVE_PORTREG 178 csport = portOutputRegister(digitalPinToPort(cs)); 179 cspinmask = digitalPinToBitMask(cs); 180 dcport = portOutputRegister(digitalPinToPort(dc)); 181 dcpinmask = digitalPinToBitMask(dc); 182#endif 183 if (!hwSPI){ 184 // set pins for software-SPI 185 pinMode(sid, OUTPUT); 186 pinMode(sclk, OUTPUT); 187#ifdef HAVE_PORTREG 188 clkport = portOutputRegister(digitalPinToPort(sclk)); 189 clkpinmask = digitalPinToBitMask(sclk); 190 mosiport = portOutputRegister(digitalPinToPort(sid)); 191 mosipinmask = digitalPinToBitMask(sid); 192#endif 193 } 194 if (hwSPI){ 195 SPI.begin(); 196#ifdef SPI_HAS_TRANSACTION 197 SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0)); 198#else 199 SPI.setClockDivider (4); 200#endif 201 } 202 } 203 else 204 { 205 // I2C Init 206 Wire.begin(); 207#ifdef __SAM3X8E__ 208 // Force 400 KHz I2C, rawr! (Uses pins 20, 21 for SDA, SCL) 209 TWI1->TWI_CWGR = 0; 210 TWI1->TWI_CWGR = ((VARIANT_MCK / (2 * 400000)) - 4) * 0x101; 211#endif 212 } 213 if ((reset) && (rst >= 0)) { 214 // Setup reset pin direction (used by both SPI and I2C) 215 pinMode(rst, OUTPUT); 216 digitalWrite(rst, HIGH); 217 // VDD (3.3V) goes high at start, lets just chill for a ms 218 delay(1); 219 // bring reset low 220 digitalWrite(rst, LOW); 221 // wait 10ms 222 delay(10); 223 // bring out of reset 224 digitalWrite(rst, HIGH); 225 // turn on VCC (9V?) 226 } 227 228 // Init sequence 229 ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE 230 ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 231 ssd1306_command(0x80); // the suggested ratio 0x80 232 233 ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8 234 ssd1306_command(SSD1306_LCDHEIGHT - 1); 235 236 ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3 237 ssd1306_command(0x0); // no offset 238 ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 239 ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D 240 if (vccstate == SSD1306_EXTERNALVCC) 241 { ssd1306_command(0x10); } 242 else 243 { ssd1306_command(0x14); } 244 ssd1306_command(SSD1306_MEMORYMODE); // 0x20 245 ssd1306_command(0x00); // 0x0 act like ks0108 246 ssd1306_command(SSD1306_SEGREMAP | 0x1); 247 ssd1306_command(SSD1306_COMSCANDEC); 248 249 #if defined SSD1306_128_32 250 ssd1306_command(SSD1306_SETCOMPINS); // 0xDA 251 ssd1306_command(0x02); 252 ssd1306_command(SSD1306_SETCONTRAST); // 0x81 253 ssd1306_command(0x8F); 254 255#elif defined SSD1306_128_64 256 ssd1306_command(SSD1306_SETCOMPINS); // 0xDA 257 ssd1306_command(0x12); 258 ssd1306_command(SSD1306_SETCONTRAST); // 0x81 259 if (vccstate == SSD1306_EXTERNALVCC) 260 { ssd1306_command(0x9F); } 261 else 262 { ssd1306_command(0xCF); } 263 264#elif defined SSD1306_96_16 265 ssd1306_command(SSD1306_SETCOMPINS); // 0xDA 266 ssd1306_command(0x2); //ada x12 267 ssd1306_command(SSD1306_SETCONTRAST); // 0x81 268 if (vccstate == SSD1306_EXTERNALVCC) 269 { ssd1306_command(0x10); } 270 else 271 { ssd1306_command(0xAF); } 272 273#endif 274 275 ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9 276 if (vccstate == SSD1306_EXTERNALVCC) 277 { ssd1306_command(0x22); } 278 else 279 { ssd1306_command(0xF1); } 280 ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB 281 ssd1306_command(0x40); 282 ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4 283 ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6 284 285 ssd1306_command(SSD1306_DEACTIVATE_SCROLL); 286 287 ssd1306_command(SSD1306_DISPLAYON);//--turn on oled panel 288} 289 290 291void Adafruit_SSD1306::invertDisplay(uint8_t i) { 292 if (i) { 293 ssd1306_command(SSD1306_INVERTDISPLAY); 294 } else { 295 ssd1306_command(SSD1306_NORMALDISPLAY); 296 } 297} 298 299void Adafruit_SSD1306::ssd1306_command(uint8_t c) { 300 if (sid != -1) 301 { 302 // SPI 303#ifdef HAVE_PORTREG 304 *csport |= cspinmask; 305 *dcport &= ~dcpinmask; 306 *csport &= ~cspinmask; 307#else 308 digitalWrite(cs, HIGH); 309 digitalWrite(dc, LOW); 310 digitalWrite(cs, LOW); 311#endif 312 fastSPIwrite(c); 313#ifdef HAVE_PORTREG 314 *csport |= cspinmask; 315#else 316 digitalWrite(cs, HIGH); 317#endif 318 } 319 else 320 { 321 // I2C 322 uint8_t control = 0x00; // Co = 0, D/C = 0 323 Wire.beginTransmission(_i2caddr); 324 Wire.write(control); 325 Wire.write(c); 326 Wire.endTransmission(); 327 } 328} 329 330// startscrollright 331// Activate a right handed scroll for rows start through stop 332// Hint, the display is 16 rows tall. To scroll the whole display, run: 333// display.scrollright(0x00, 0x0F) 334void Adafruit_SSD1306::startscrollright(uint8_t start, uint8_t stop){ 335 ssd1306_command(SSD1306_RIGHT_HORIZONTAL_SCROLL); 336 ssd1306_command(0X00); 337 ssd1306_command(start); 338 ssd1306_command(0X00); 339 ssd1306_command(stop); 340 ssd1306_command(0X00); 341 ssd1306_command(0XFF); 342 ssd1306_command(SSD1306_ACTIVATE_SCROLL); 343} 344 345// startscrollleft 346// Activate a right handed scroll for rows start through stop 347// Hint, the display is 16 rows tall. To scroll the whole display, run: 348// display.scrollright(0x00, 0x0F) 349void Adafruit_SSD1306::startscrollleft(uint8_t start, uint8_t stop){ 350 ssd1306_command(SSD1306_LEFT_HORIZONTAL_SCROLL); 351 ssd1306_command(0X00); 352 ssd1306_command(start); 353 ssd1306_command(0X00); 354 ssd1306_command(stop); 355 ssd1306_command(0X00); 356 ssd1306_command(0XFF); 357 ssd1306_command(SSD1306_ACTIVATE_SCROLL); 358} 359 360// startscrolldiagright 361// Activate a diagonal scroll for rows start through stop 362// Hint, the display is 16 rows tall. To scroll the whole display, run: 363// display.scrollright(0x00, 0x0F) 364void Adafruit_SSD1306::startscrolldiagright(uint8_t start, uint8_t stop){ 365 ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); 366 ssd1306_command(0X00); 367 ssd1306_command(SSD1306_LCDHEIGHT); 368 ssd1306_command(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL); 369 ssd1306_command(0X00); 370 ssd1306_command(start); 371 ssd1306_command(0X00); 372 ssd1306_command(stop); 373 ssd1306_command(0X01); 374 ssd1306_command(SSD1306_ACTIVATE_SCROLL); 375} 376 377// startscrolldiagleft 378// Activate a diagonal scroll for rows start through stop 379// Hint, the display is 16 rows tall. To scroll the whole display, run: 380// display.scrollright(0x00, 0x0F) 381void Adafruit_SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop){ 382 ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); 383 ssd1306_command(0X00); 384 ssd1306_command(SSD1306_LCDHEIGHT); 385 ssd1306_command(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL); 386 ssd1306_command(0X00); 387 ssd1306_command(start); 388 ssd1306_command(0X00); 389 ssd1306_command(stop); 390 ssd1306_command(0X01); 391 ssd1306_command(SSD1306_ACTIVATE_SCROLL); 392} 393 394void Adafruit_SSD1306::stopscroll(void){ 395 ssd1306_command(SSD1306_DEACTIVATE_SCROLL); 396} 397 398// Dim the display 399// dim = true: display is dimmed 400// dim = false: display is normal 401void Adafruit_SSD1306::dim(boolean dim) { 402 uint8_t contrast; 403 404 if (dim) { 405 contrast = 1; //0; // Dimmed display - If = zero, the display is off. 406 } else { 407 if (_vccstate == SSD1306_EXTERNALVCC) { 408 contrast = 0x9F; 409 } else { 410 contrast = 0xCF; 411 } 412 } 413 // the range of contrast to too small to be really useful 414 // it is useful to dim the display 415 ssd1306_command(SSD1306_SETCONTRAST); 416 ssd1306_command(contrast); 417} 418 419void Adafruit_SSD1306::display(void) { 420 ssd1306_command(SSD1306_COLUMNADDR); 421 ssd1306_command(0); // Column start address (0 = reset) 422 ssd1306_command(SSD1306_LCDWIDTH-1); // Column end address (127 = reset) 423 424 ssd1306_command(SSD1306_PAGEADDR); 425 ssd1306_command(0); // Page start address (0 = reset) 426 #if SSD1306_LCDHEIGHT == 64 427 ssd1306_command(7); // Page end address 428 #endif 429 #if SSD1306_LCDHEIGHT == 32 430 ssd1306_command(3); // Page end address 431 #endif 432 #if SSD1306_LCDHEIGHT == 16 433 ssd1306_command(1); // Page end address 434 #endif 435 436 if (sid != -1) 437 { 438 // SPI 439#ifdef HAVE_PORTREG 440 *csport |= cspinmask; 441 *dcport |= dcpinmask; 442 *csport &= ~cspinmask; 443#else 444 digitalWrite(cs, HIGH); 445 digitalWrite(dc, HIGH); 446 digitalWrite(cs, LOW); 447#endif 448 449 for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { 450 fastSPIwrite(buffer[i]); 451 } 452#ifdef HAVE_PORTREG 453 *csport |= cspinmask; 454#else 455 digitalWrite(cs, HIGH); 456#endif 457 } 458 else 459 { 460 // save I2C bitrate 461#ifdef TWBR 462 uint8_t twbrbackup = TWBR; 463 TWBR = 12; // upgrade to 400KHz! 464#endif 465 466 //Serial.println(TWBR, DEC); 467 //Serial.println(TWSR & 0x3, DEC); 468 469 // I2C 470 for (uint16_t i=0; i<(SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8); i++) { 471 // send a bunch of data in one xmission 472 Wire.beginTransmission(_i2caddr); 473 WIRE_WRITE(0x40); 474 for (uint8_t x=0; x<16; x++) { 475 WIRE_WRITE(buffer[i]); 476 i++; 477 } 478 i--; 479 Wire.endTransmission(); 480 } 481#ifdef TWBR 482 TWBR = twbrbackup; 483#endif 484 } 485} 486 487// clear everything 488void Adafruit_SSD1306::clearDisplay(void) { 489 memset(buffer, 0, (SSD1306_LCDWIDTH*SSD1306_LCDHEIGHT/8)); 490} 491 492 493inline void Adafruit_SSD1306::fastSPIwrite(uint8_t d) { 494 495 if(hwSPI) { 496 (void)SPI.transfer(d); 497 } else { 498 for(uint8_t bit = 0x80; bit; bit >>= 1) { 499#ifdef HAVE_PORTREG 500 *clkport &= ~clkpinmask; 501 if(d & bit) *mosiport |= mosipinmask; 502 else *mosiport &= ~mosipinmask; 503 *clkport |= clkpinmask; 504#else 505 digitalWrite(sclk, LOW); 506 if(d & bit) digitalWrite(sid, HIGH); 507 else digitalWrite(sid, LOW); 508 digitalWrite(sclk, HIGH); 509#endif 510 } 511 } 512} 513 514void Adafruit_SSD1306::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) { 515 boolean bSwap = false; 516 switch(rotation) { 517 case 0: 518 // 0 degree rotation, do nothing 519 break; 520 case 1: 521 // 90 degree rotation, swap x & y for rotation, then invert x 522 bSwap = true; 523 ssd1306_swap(x, y); 524 x = WIDTH - x - 1; 525 break; 526 case 2: 527 // 180 degree rotation, invert x and y - then shift y around for height. 528 x = WIDTH - x - 1; 529 y = HEIGHT - y - 1; 530 x -= (w-1); 531 break; 532 case 3: 533 // 270 degree rotation, swap x & y for rotation, then invert y and adjust y for w (not to become h) 534 bSwap = true; 535 ssd1306_swap(x, y); 536 y = HEIGHT - y - 1; 537 y -= (w-1); 538 break; 539 } 540 541 if(bSwap) { 542 drawFastVLineInternal(x, y, w, color); 543 } else { 544 drawFastHLineInternal(x, y, w, color); 545 } 546} 547 548void Adafruit_SSD1306::drawFastHLineInternal(int16_t x, int16_t y, int16_t w, uint16_t color) { 549 // Do bounds/limit checks 550 if(y < 0 || y >= HEIGHT) { return; } 551 552 // make sure we don't try to draw below 0 553 if(x < 0) { 554 w += x; 555 x = 0; 556 } 557 558 // make sure we don't go off the edge of the display 559 if( (x + w) > WIDTH) { 560 w = (WIDTH - x); 561 } 562 563 // if our width is now negative, punt 564 if(w <= 0) { return; } 565 566 // set up the pointer for movement through the buffer 567 register uint8_t *pBuf = buffer; 568 // adjust the buffer pointer for the current row 569 pBuf += ((y/8) * SSD1306_LCDWIDTH); 570 // and offset x columns in 571 pBuf += x; 572 573 register uint8_t mask = 1 << (y&7); 574 575 switch (color) 576 { 577 case WHITE: while(w--) { *pBuf++ |= mask; }; break; 578 case BLACK: mask = ~mask; while(w--) { *pBuf++ &= mask; }; break; 579 case INVERSE: while(w--) { *pBuf++ ^= mask; }; break; 580 } 581} 582 583void Adafruit_SSD1306::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) { 584 bool bSwap = false; 585 switch(rotation) { 586 case 0: 587 break; 588 case 1: 589 // 90 degree rotation, swap x & y for rotation, then invert x and adjust x for h (now to become w) 590 bSwap = true; 591 ssd1306_swap(x, y); 592 x = WIDTH - x - 1; 593 x -= (h-1); 594 break; 595 case 2: 596 // 180 degree rotation, invert x and y - then shift y around for height. 597 x = WIDTH - x - 1; 598 y = HEIGHT - y - 1; 599 y -= (h-1); 600 break; 601 case 3: 602 // 270 degree rotation, swap x & y for rotation, then invert y 603 bSwap = true; 604 ssd1306_swap(x, y); 605 y = HEIGHT - y - 1; 606 break; 607 } 608 609 if(bSwap) { 610 drawFastHLineInternal(x, y, h, color); 611 } else { 612 drawFastVLineInternal(x, y, h, color); 613 } 614} 615 616 617void Adafruit_SSD1306::drawFastVLineInternal(int16_t x, int16_t __y, int16_t __h, uint16_t color) { 618 619 // do nothing if we're off the left or right side of the screen 620 if(x < 0 || x >= WIDTH) { return; } 621 622 // make sure we don't try to draw below 0 623 if(__y < 0) { 624 // __y is negative, this will subtract enough from __h to account for __y being 0 625 __h += __y; 626 __y = 0; 627 628 } 629 630 // make sure we don't go past the height of the display 631 if( (__y + __h) > HEIGHT) { 632 __h = (HEIGHT - __y); 633 } 634 635 // if our height is now negative, punt 636 if(__h <= 0) { 637 return; 638 } 639 640 // this display doesn't need ints for coordinates, use local byte registers for faster juggling 641 register uint8_t y = __y; 642 register uint8_t h = __h; 643 644 645 // set up the pointer for fast movement through the buffer 646 register uint8_t *pBuf = buffer; 647 // adjust the buffer pointer for the current row 648 pBuf += ((y/8) * SSD1306_LCDWIDTH); 649 // and offset x columns in 650 pBuf += x; 651 652 // do the first partial byte, if necessary - this requires some masking 653 register uint8_t mod = (y&7); 654 if(mod) { 655 // mask off the high n bits we want to set 656 mod = 8-mod; 657 658 // note - lookup table results in a nearly 10% performance improvement in fill* functions 659 // register uint8_t mask = ~(0xFF >> (mod)); 660 static uint8_t premask[8] = {0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; 661 register uint8_t mask = premask[mod]; 662 663 // adjust the mask if we're not going to reach the end of this byte 664 if( h < mod) { 665 mask &= (0XFF >> (mod-h)); 666 } 667 668 switch (color) 669 { 670 case WHITE: *pBuf |= mask; break; 671 case BLACK: *pBuf &= ~mask; break; 672 case INVERSE: *pBuf ^= mask; break; 673 } 674 675 // fast exit if we're done here! 676 if(h<mod) { return; } 677 678 h -= mod; 679 680 pBuf += SSD1306_LCDWIDTH; 681 } 682 683 684 // write solid bytes while we can - effectively doing 8 rows at a time 685 if(h >= 8) { 686 if (color == INVERSE) { // separate copy of the code so we don't impact performance of the black/white write version with an extra comparison per loop 687 do { 688 *pBuf=~(*pBuf); 689 690 // adjust the buffer forward 8 rows worth of data 691 pBuf += SSD1306_LCDWIDTH; 692 693 // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now) 694 h -= 8; 695 } while(h >= 8); 696 } 697 else { 698 // store a local value to work with 699 register uint8_t val = (color == WHITE) ? 255 : 0; 700 701 do { 702 // write our value in 703 *pBuf = val; 704 705 // adjust the buffer forward 8 rows worth of data 706 pBuf += SSD1306_LCDWIDTH; 707 708 // adjust h & y (there's got to be a faster way for me to do this, but this should still help a fair bit for now) 709 h -= 8; 710 } while(h >= 8); 711 } 712 } 713 714 // now do the final partial byte, if necessary 715 if(h) { 716 mod = h & 7; 717 // this time we want to mask the low bits of the byte, vs the high bits we did above 718 // register uint8_t mask = (1 << mod) - 1; 719 // note - lookup table results in a nearly 10% performance improvement in fill* functions 720 static uint8_t postmask[8] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; 721 register uint8_t mask = postmask[mod]; 722 switch (color) 723 { 724 case WHITE: *pBuf |= mask; break; 725 case BLACK: *pBuf &= ~mask; break; 726 case INVERSE: *pBuf ^= mask; break; 727 } 728 } 729} 730
TicTacToe - Serial Monitor version
arduino
This sketch requires the Arduino board only. You can play using the Arduino IDE serial monitor
1/************************************************************************** 2 3 * 4 * TicTacToe - Serial monitor version. 5 * Note: the OLED version code 6 is better described; if you have any 7 * doubt about any instruction, please 8 take a look to that version. 9 * 10 * It only requires an Arduino (tested 11 with Nano and Uno, but it 12 * should work with (almost?) any Arduino and other 13 clones 14 * 15 * To use it, you need to connect the Arduino to USB and call 16 the 17 * Serial monitor from inside Arduino IDE: Menu tools -> serial monitor 18 19 * (or pressing Ctrl + Shift + M) 20 * 21 * Move using the 1...9 keys (plus 22 Enter) from inside the Serial Monitor: 23 * 24 * 1 | 2 | 3 25 * ---+---+--- 26 27 * 4 | 5 | 6 28 * ---+---+--- 29 * 7 | 8 | 9 30 * 31 * Giovanni Verrua, 32 april 2022 33**************************************************************************/ 34 35 36int 37 gameStatus = 0; 38int whosplaying = 0; //0 = Arduino, 1 = Human 39 40int winner 41 = -1; //-1 = Playing, 0 = Draw, 1 = Human, 2 = CPU 42 43 44int board[]={0,0,0, 45 46 0,0,0, 47 0,0,0}; //0 = blank, 1 = human (circle), 48 2 = computer (cross) 49 50 51 52 53 54//-------------------------------------------------------------------------------------------------------- 55void 56 playhuman() { 57 58 int humanMove = -1; 59 bool stayInLoop = 60 true; 61 62 while (stayInLoop) { 63 64 Serial.println(F("\ 65Make 66 your move\ 67")); 68 69 while (Serial.available() && Serial.read()); 70 // empty buffer 71 while (!Serial.available()); // wait 72 for data 73 74 if (Serial.available()) { //data available. 75 76 77 int humanMove = Serial.read() - 48; //serial.read() return the 78 ascii value of the pressed key. The "1" ascii value is 49. 79 80 81 Serial.println(humanMove); 82 83 if (humanMove 84 >=0 && humanMove <=9) { 85 86 if (board[humanMove-1] !=0) { 87 88 Serial.println(F("\ 89Error - Cell already in use")); 90 91 stayInLoop = true; 92 } 93 else 94 { 95 stayInLoop = false; 96 board[humanMove-1] 97 = 1; //remember: the player uses the 1..9 keys, but the array index starts by 0 98 (9 cells = 0..8) 99 } 100 } 101 else 102 { 103 Serial.println(F("\ 104type error (just 1 to 9)")); 105 106 stayInLoop = true; 107 } 108 109 110 } 111 } 112 113 114 115 116} 117 118 119//-------------------------------------------------------------------------------------------------------- 120void 121 playcpu() { 122 123 int cpumove = checkboard(2); //2 = cpu let's 124 check if there's a cpu's winner move 125 126 if (cpumove >=0) { 127 board[cpumove] 128 = 2; //cpu's winner move 129 } 130 else { 131 cpumove = checkboard(1); 132 //1=player check if the player has a chance to win (2 circles and an empty cell 133 in a row) 134 if (cpumove >=0) { 135 board[cpumove] = 2; 136 //this move will break the player's winner move 137 } 138 139 140 //there's no possible winner move neither for the cpu, nor for the human, 141 I will put an "X" in a random cell 142 while (cpumove < 0) { 143 144 int randomMove = random(10); 145 if (randomMove >=0 && randomMove 146 <=8 && board[randomMove] == 0) { 147 cpumove = randomMove; 148 } 149 150 } 151 board[cpumove] = 2; 152 } 153} 154 155 156//-------------------------------------------------------------------------------------------------------- 157int 158 checkboard(int x){ //x = 1 -> player, x = 2 -> cpu 159 160//full case 161 162 163 if (board[0]==0 && board[1]==x && board[2]==x) return 0; // 0 1 1 164 165 // . . . 166 167 // . . . 168 169 170 else if 171 (board[0]==x && board[1]==0 && board[2]==x) return 1; // 1 0 1 172 // 173 . . . 174 // 175 . . . 176 177 178 else if (board[0]==x && board[1]==x && board[2]==0) return 2; // 1 1 0 179 180 // . . . 181 182 // . . . 183 184 //------------------------------------------------- 185 else if (board[3]==0 186 && board[4]==x && board[5]==x) return 3; // . . . 187 // 188 0 1 1 189 // 190 . . . 191 192 193 else if (board[3]==x && board[4]==0 && board[5]==x) return 4; // . . . 194 195 // 1 0 1 196 197 // . . . 198 199 200 else if (board[3]==x && board[4]==x && board[5]==0) return 5; // . . . 201 202 // 1 1 0 203 204 // . . . 205 206 //------------------------------------------------- 207 else if (board[6]==0 208 && board[7]==x && board[8]==x) return 6; // . . . 209 // 210 . . . 211 // 212 0 1 1 213 214 215 else if (board[6]==x && board[7]==0 && board[8]==x) return 7; // . . . 216 217 // . . . 218 219 // 1 0 1 220 221 222 else if (board[6]==x && board[7]==x && board[8]==0) return 8; // . . . 223 224 // . . . 225 226 // 1 1 0 227 228 229 //------------------------------------------------- 230 else if (board[0]==0 231 && board[3]==x && board[6]==x) return 0; // 0 . . 232 // 233 1 . . 234 // 235 1 . . 236 237 else if (board[0]==x && board[3]==0 && board[6]==x) return 3; 238 // 1 . . 239 // 240 0 . . 241 // 242 1 . . 243 244 else if (board[0]==x && board[3]==x && board[6]==0) return 6; 245 // 1 . . 246 // 247 1 . . 248 // 249 0 . . 250 251 252 //------------------------------------------------- 253 else if (board[1]==0 254 && board[4]==x && board[7]==x) return 1; // . 0 . 255 // 256 . 1 . 257 // 258 . 1 . 259 260 else if (board[1]==x && board[4]==0 && board[7]==x) return 4; 261 // . 1 . 262 // 263 . 0 . 264 // 265 . 1 . 266 267 else if (board[1]==x && board[4]==x && board[7]==0) return 268 7; // . 1 . 269 // 270 . 1 . 271 // 272 . 0 . 273 274 275 //------------------------------------------------- 276 else if (board[2]==0 277 && board[5]==x && board[8]==x) return 2; // . . 0 278 // 279 . . 1 280 // 281 . . 1 282 283 else if (board[2]==x && board[5]==0 && board[8]==x) return 284 5; // . . 1 285 // 286 . . 0 287 // 288 . . 1 289 290 else if (board[2]==x && board[5]==x && board[8]==0) return 291 8; // . . 1 292 // 293 . . 1 294 // 295 . . 0 296 297 298 //------------------------------------------------- 299 else if (board[0]==0 300 && board[4]==x && board[8]==x) return 0; // 0 . . 301 // 302 . 1 . 303 // 304 . . 1 305 306 else if (board[0]==x && board[4]==0 && board[8]==x) return 307 4; // 1 . . 308 // 309 . 0 . 310 // 311 . . 1 312 313 else if (board[0]==x && board[4]==x && board[8]==0) return 314 8; // 1 . . 315 // 316 . 1 . 317 // 318 . . 0 319 320 //------------------------------------------------- 321 else if 322 (board[2]==0 && board[4]==x && board[6]==x) return 2; // . . 0 323 // 324 . 1 . 325 // 326 1 . . 327 328 else if (board[2]==x && board[4]==0 && board[6]==x) return 4; 329 // . . 1 330 // 331 . 0 . 332 // 333 1 . . 334 335 else if (board[2]==x && board[4]==x && board[6]==0) return 336 6; // . . 1 337 // 338 . 1 . 339 // 340 0 . . 341 342 else return 343 -1; 344} 345 346 347//-------------------------------------------------------------------------------------------- 348 349 350void 351 checkWinner() { //check the board to see if there is a winner 352 353 winner 354 = 3; //3=draft, 1= winner->player, 2=winner->cpu 355 356 // circles win? 357 358 if (board[0]==1 && board