package org.myrobotlab.service; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.service.interfaces.DeviceController; import org.myrobotlab.service.interfaces.I2CControl; import org.myrobotlab.service.interfaces.I2CController; import org.myrobotlab.service.interfaces.ServiceInterface; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import static org.myrobotlab.service.data.OledSsd1306Data.SSD1306_128_64Data; import static org.myrobotlab.service.data.OledSsd1306Data.SSD1306_128_32Data; import static org.myrobotlab.service.data.OledSsd1306Data.SSD1306_96_16Data; import static org.myrobotlab.service.data.OledSsd1306Data.FONT; import org.slf4j.Logger; /** * * OledSsd1306 - This service can be used to drive a OLED display using the i2c * protocol It's built for the SSD1306 driver * * @author Mats Onnerby * * More Info : https://www.adafruit.com/product/326 References : * https://github.com/adafruit/Adafruit_SSD1306 * * This service builds is a conversion from of the Arduino library above * to Java * * Some OLED's are wired for SPI and need to be changed according to * this instruction so that you can use the i2c protocol ( as * implemented in this program ) * * http://electronics.stackexchange.com/questions/164680/ssd1306-display * -isp-connection-or-i2c-according-to-resistors * * NB. Some OLED's need a reset pulse at power on. It can be generated * using one of the pin's on the Arduino or by adding a 100nF capacitor * between the reset pin and GND, and a 10K resistor betwen the reset * pin and VCC * */ public class OledSsd1306 extends Service implements I2CControl { private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(OledSsd1306.class.getCanonicalName()); public static final int HIGH = 0x1; public static final int LOW = 0x0; public static final int INPUT = 0x0; public static final int OUTPUT = 0x1; public List<String> controllers = new ArrayList<String>(); public String controllerName; transient public I2CController controller; // Remove // this // when // PinArrayControl // has // been // implementer // in // Raspi public boolean isControllerSet = false; public List<String> deviceAddressList = Arrays.asList("0x3C", "0x3D"); public String deviceAddress = "0x3C"; public List<String> deviceBusList = Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7"); public String deviceBus = "1"; // private boolean isAttached = false; // Constants for the SSD1306 public static short SSD1306_SETCONTRAST = 0x81; public static short SSD1306_DISPLAYALLON_RESUME = 0xA4; public static short SSD1306_DISPLAYALLON = 0xA5; public static short SSD1306_NORMALDISPLAY = 0xA6; public static short SSD1306_INVERTDISPLAY = 0xA7; public static short SSD1306_DISPLAYOFF = 0xAE; public static short SSD1306_DISPLAYON = 0xAF; public static short SSD1306_SETDISPLAYOFFSET = 0xD3; public static short SSD1306_SETCOMPINS = 0xDA; public static short SSD1306_SETVCOMDETECT = 0xDB; public static short SSD1306_SETDISPLAYCLOCKDIV = 0xD5; public static short SSD1306_SETPRECHARGE = 0xD9; public static short SSD1306_SETMULTIPLEX = 0xA8; public static short SSD1306_SETLOWCOLUMN = 0x00; public static short SSD1306_SETHIGHCOLUMN = 0x10; public static short SSD1306_SETSTARTLINE = 0x40; public static short SSD1306_MEMORYMODE = 0x20; public static short SSD1306_COLUMNADDR = 0x21; public static short SSD1306_PAGEADDR = 0x22; public static short SSD1306_COMSCANINC = 0xC0; public static short SSD1306_COMSCANDEC = 0xC8; public static short SSD1306_SEGREMAP = 0xA0; public static short SSD1306_CHARGEPUMP = 0x8D; public static short SSD1306_EXTERNALVCC = 0x1; public static short SSD1306_SWITCHCAPVCC = 0x2; // The different types of OLED's supported public static int SSD1306_128_64 = 0; public static int SSD1306_128_32 = 1; public static int SSD1306_96_16 = 2; public int oledType = SSD1306_128_64; // Set // default // oledType // to // 128*64 public static final int BLACK = 0; public static final int WHITE = 1; public static final int INVERSE = 2; // Scrolling #defines public static int SSD1306_ACTIVATE_SCROLL = 0x2F; public static int SSD1306_DEACTIVATE_SCROLL = 0x2E; public static int SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3; public static int SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26; public static int SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27; public static int SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29; public static int SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A; // Buffer for the OLED public int SSD1306_LCDWIDTH = 128; public int SSD1306_LCDHEIGHT = 64; public int[] buffer; // pin private int vccstate; // vccstate // // // vccstate private int rotation; static int premask[] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; static int postmask[] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; public static void main(String[] args) { LoggingFactory.getInstance().configure(); LoggingFactory.getInstance().setLevel(Level.DEBUG); try { OledSsd1306 oledSsd1306 = (OledSsd1306) Runtime.start("OledSsd1306", "OledSsd1306"); Runtime.start("gui", "GUIService"); } catch (Exception e) { Logging.logError(e); } } public OledSsd1306(String n) { super(n); refreshControllers(); subscribe(Runtime.getInstance().getName(), "registered", this.getName(), "onRegistered"); setDisplayType(SSD1306_128_64); } public void onRegistered(ServiceInterface s) { refreshControllers(); broadcastState(); } public void refreshControllers() { controllers = Runtime.getServiceNamesFromInterface(I2CController.class); controllers.remove(this.getName()); broadcastState(); } public void setDeviceBus(String deviceBus) { this.deviceBus = deviceBus; broadcastState(); } public void setDeviceAddress(String deviceAddress) { this.deviceAddress = deviceAddress; broadcastState(); } /** * This methods sets the i2c Controller that will be used to communicate with * the i2c device */ // @Override public boolean setController(String controllerName, String deviceBus, String deviceAddress) { this.controllerName = controllerName; return setController((I2CController) Runtime.getService(controllerName), deviceBus, deviceAddress); } public boolean setController(String controllerName) { this.controllerName = controllerName; return setController((I2CController) Runtime.getService(controllerName), this.deviceBus, this.deviceAddress); } public boolean setController(I2CController controller) { return setController(controller, this.deviceBus, this.deviceAddress); } /** * This methods sets the i2c Controller that will be used to communicate with * the i2c device */ public boolean setController(I2CController controller, String deviceBus, String deviceAddress) { if (controller == null) { error("setting null as controller"); return false; } log.info(String.format("%s setController %s", getName(), controllerName)); controllerName = controller.getName(); this.controller = controller; this.deviceBus = deviceBus; this.deviceAddress = deviceAddress; createDevice(); isControllerSet = true; broadcastState(); return true; } /** * This method creates the i2c device */ boolean createDevice() { if (controller != null) { controller.createI2cDevice(this, Integer.parseInt(deviceBus), Integer.decode(deviceAddress)); } log.info(String.format("Creating device on bus: %s address %s", deviceBus, deviceAddress)); return true; } public void unsetController() { /* * controller = null; controllerName = null; this.deviceBus = null; * this.deviceAddress = null; */ isControllerSet = false; broadcastState(); } public I2CController getController() { return controller; } public String getControllerName() { String controlerName = null; if (controller != null) { controlerName = controller.getName(); } return controlerName; } /* * public boolean isAttached() { return isAttached; } */ /** * Initiate the buffer with data for the size of the OLED screen * * @param displayType */ public void setDisplayType(int displayType) { if (displayType == SSD1306_128_64) { SSD1306_LCDWIDTH = 128; SSD1306_LCDHEIGHT = 64; buffer = new int[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8]; buffer = SSD1306_128_64Data.clone(); } else if (displayType == SSD1306_128_32) { SSD1306_LCDWIDTH = 128; SSD1306_LCDHEIGHT = 32; buffer = new int[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8]; buffer = SSD1306_128_32Data.clone(); } else if (displayType == SSD1306_96_16) { SSD1306_LCDWIDTH = 96; SSD1306_LCDHEIGHT = 16; buffer = new int[SSD1306_LCDHEIGHT * SSD1306_LCDWIDTH / 8]; buffer = SSD1306_96_16Data.clone(); } else { log.error(String.format("DisplayType %s not implemented.", displayType)); } } int getWidth() { return SSD1306_LCDWIDTH; } int getHeight() { return SSD1306_LCDHEIGHT; } public synchronized void setPixel(int x, int y, boolean on) { final int pos = x + (y / 8) * SSD1306_LCDWIDTH; if (on) { this.buffer[pos] |= (1 << (y & 0x07)); } else { this.buffer[pos] &= ~(1 << (y & 0x07)); } } void drawBitmap(int x, int y, int[] bitmap, int w, int h, boolean on) { int i, j, byteWidth = (w + 7) / 8; int aByte = 0; for (j = 0; j < h; j++) { for (i = 0; i < w; i++) { if ((i & 7)>0) { aByte <<= 1; } else { aByte = bitmap[j * byteWidth + i / 8]; } if ((aByte & 0x80)>0) { setPixel(x + i, y + j, on); } else { setPixel(x + i, y + j, !on); } } } } public synchronized void drawString(String string, int x, int y, boolean on) { int posX = x; int posY = y; for (char c : string.toCharArray()) { if (c == '\n') { posY += 8; posX = x; } if (posX >= 0 && posX + 5 < this.getWidth() && posY >= 0 && posY + 7 < this.getHeight()) { drawChar(c, posX, posY, on); } posX += 6; } } public synchronized void drawStringCentered(String string, int y, boolean on) { final int strSizeX = string.length() * 5 + string.length() - 1; final int x = (this.getWidth() - strSizeX) / 2; drawString(string, x, y, on); } public synchronized void clearRect(int x, int y, int width, int height, boolean on) { for (int posX = x; posX < x + width; ++posX) { for (int posY = y; posY < y + height; ++posY) { setPixel(posX, posY, on); } } } public synchronized void drawChar(char c, int x, int y, boolean on) { if (c > 255) { c = '?'; } for (int i = 0; i < 5; ++i) { int line = FONT[(c * 5) + i]; for (int j = 0; j < 8; ++j) { if ((line & 0x01) > 0) { setPixel(x + i, y + j, on); } line >>= 1; } } } /** * draws the given image over the current image buffer. The image is * automatically converted to a binary image (if it not already is). * <p/> * Note that the current buffer is not cleared before, so if you want the * image to completely overwrite the current display content you need to call * clear() before. * * @param image * @param x * @param y */ public synchronized void drawImage(BufferedImage image, int x, int y) { BufferedImage tmpImage = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_BYTE_BINARY); tmpImage.getGraphics().drawImage(image, x, y, null); int index = 0; int pixelval; final byte[] pixels = ((DataBufferByte) tmpImage.getRaster().getDataBuffer()).getData(); for (int posY = 0; posY < SSD1306_LCDHEIGHT; posY++) { for (int posX = 0; posX < SSD1306_LCDWIDTH / 8; posX++) { for (int bit = 0; bit < 8; bit++) { pixelval = (byte) ((pixels[index / 8] >> (8 - bit)) & 0x01); setPixel(posX * 8 + bit, posY, pixelval > 0); index++; } } } } void begin(int vccstate) { this.vccstate = vccstate; // Init sequence ssd1306_command(SSD1306_DISPLAYOFF); // 0xAE ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV); // 0xD5 ssd1306_command(0x80); // the suggested ratio 0x80 ssd1306_command(SSD1306_SETMULTIPLEX); // 0xA8 ssd1306_command(SSD1306_LCDHEIGHT - 1); ssd1306_command(SSD1306_SETDISPLAYOFFSET); // 0xD3 ssd1306_command(0x0); // no offset ssd1306_command(SSD1306_SETSTARTLINE | 0x0); // line #0 ssd1306_command(SSD1306_CHARGEPUMP); // 0x8D if (vccstate == SSD1306_EXTERNALVCC) { ssd1306_command(0x10); } else { ssd1306_command(0x14); } ssd1306_command(SSD1306_MEMORYMODE); // 0x20 ssd1306_command(0x00); // 0x0 act like ks0108 ssd1306_command(SSD1306_SEGREMAP | 0x1); ssd1306_command(SSD1306_COMSCANDEC); if (oledType == SSD1306_128_32) { ssd1306_command(SSD1306_SETCOMPINS); // 0xDA ssd1306_command(0x02); ssd1306_command(SSD1306_SETCONTRAST); // 0x81 ssd1306_command(0x8F); } else if (oledType == SSD1306_128_64) { ssd1306_command(SSD1306_SETCOMPINS); // 0xDA ssd1306_command(0x12); ssd1306_command(SSD1306_SETCONTRAST); // 0x81 if (vccstate == SSD1306_EXTERNALVCC) { ssd1306_command(0x9F); } else { ssd1306_command(0xCF); } } else if (oledType == SSD1306_96_16) { ssd1306_command(SSD1306_SETCOMPINS); // 0xDA ssd1306_command(0x2); // ada x12 ssd1306_command(SSD1306_SETCONTRAST); // 0x81 if (vccstate == SSD1306_EXTERNALVCC) { ssd1306_command(0x10); } else { ssd1306_command(0xAF); } } ssd1306_command(SSD1306_SETPRECHARGE); // 0xd9 if (vccstate == SSD1306_EXTERNALVCC) { ssd1306_command(0x22); } else { ssd1306_command(0xF1); } ssd1306_command(SSD1306_SETVCOMDETECT); // 0xDB ssd1306_command(0x40); ssd1306_command(SSD1306_DISPLAYALLON_RESUME); // 0xA4 ssd1306_command(SSD1306_NORMALDISPLAY); // 0xA6 ssd1306_command(SSD1306_DEACTIVATE_SCROLL); ssd1306_command(SSD1306_DISPLAYON);// --turn on oled panel } void invertDisplay(boolean i) { if (i) { ssd1306_command(SSD1306_INVERTDISPLAY); } else { ssd1306_command(SSD1306_NORMALDISPLAY); } } void ssd1306_command(int c) { // I2C byte control = 0x00; // Co = 0, D/C = 0 byte buffer[] = { control, (byte) c }; controller.i2cWrite((I2CControl) this, Integer.parseInt(deviceBus), Integer.decode(deviceAddress), buffer, buffer.length); } // startscrollright // Activate a right handed scroll for rows start through stop // Hint, the display is 16 rows tall. To scroll the whole display, run: // display.scrollright(0x00, 0x0F) void startscrollright(int start, int stop) { ssd1306_command(SSD1306_RIGHT_HORIZONTAL_SCROLL); ssd1306_command(0X00); ssd1306_command(start); ssd1306_command(0X00); ssd1306_command(stop); ssd1306_command(0X00); ssd1306_command(0XFF); ssd1306_command(SSD1306_ACTIVATE_SCROLL); } // startscrollleft // Activate a right handed scroll for rows start through stop // Hint, the display is 16 rows tall. To scroll the whole display, run: // display.scrollright(0x00, 0x0F) void startscrollleft(int start, int stop) { ssd1306_command(SSD1306_LEFT_HORIZONTAL_SCROLL); ssd1306_command(0X00); ssd1306_command(start); ssd1306_command(0X00); ssd1306_command(stop); ssd1306_command(0X00); ssd1306_command(0XFF); ssd1306_command(SSD1306_ACTIVATE_SCROLL); } // startscrolldiagright // Activate a diagonal scroll for rows start through stop // Hint, the display is 16 rows tall. To scroll the whole display, run: // display.scrollright(0x00, 0x0F) void startscrolldiagright(int start, int stop) { ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); ssd1306_command(0X00); ssd1306_command(SSD1306_LCDHEIGHT); ssd1306_command(SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL); ssd1306_command(0X00); ssd1306_command(start); ssd1306_command(0X00); ssd1306_command(stop); ssd1306_command(0X01); ssd1306_command(SSD1306_ACTIVATE_SCROLL); } // startscrolldiagleft // Activate a diagonal scroll for rows start through stop // Hint, the display is 16 rows tall. To scroll the whole display, run: // display.scrollright(0x00, 0x0F) void startscrolldiagleft(int start, int stop) { ssd1306_command(SSD1306_SET_VERTICAL_SCROLL_AREA); ssd1306_command(0X00); ssd1306_command(SSD1306_LCDHEIGHT); ssd1306_command(SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL); ssd1306_command(0X00); ssd1306_command(start); ssd1306_command(0X00); ssd1306_command(stop); ssd1306_command(0X01); ssd1306_command(SSD1306_ACTIVATE_SCROLL); } void stopscroll() { ssd1306_command(SSD1306_DEACTIVATE_SCROLL); } // Dim the display // dim = true: display is dimmed // dim = false: display is normal void dim(boolean dim) { int contrast; if (dim) { contrast = 0; // Dimmed display } else { if (vccstate == SSD1306_EXTERNALVCC) { contrast = 0x9F; } else { contrast = 0xCF; } } // the range of contrast to too small to be really useful // it is useful to dim the display ssd1306_command(SSD1306_SETCONTRAST); ssd1306_command(contrast); } public void display(int[] image) { buffer = image.clone(); display(); } public void display() { ssd1306_command(SSD1306_COLUMNADDR); ssd1306_command(0); // Column start address (0 = reset) ssd1306_command(SSD1306_LCDWIDTH - 1); // Column end address (127 = reset) ssd1306_command(SSD1306_PAGEADDR); ssd1306_command(0); // Page start address (0 = reset) if (SSD1306_LCDHEIGHT == 64) { ssd1306_command(7); // Page end address } if (SSD1306_LCDHEIGHT == 32) { ssd1306_command(3); // Page end address } if (SSD1306_LCDHEIGHT == 16) { ssd1306_command(1); // Page end address } // I2C for (int i = 0; i < (SSD1306_LCDWIDTH * SSD1306_LCDHEIGHT / 8) - 1; i++) { // send a bunch of data in one xmission // Wire.beginTransmission(_i2caddr); byte writeBuffer[] = new byte[17]; int writeBufferIx = 0; writeBuffer[writeBufferIx] = 0x40; for (int x = 0; x < 16; x++) { writeBufferIx++; writeBuffer[writeBufferIx] = (byte) buffer[i]; // WIRE_WRITE(buffer[i]); i++; } i--; // Wire.endTransmission(); controller.i2cWrite((I2CControl) this, Integer.parseInt(deviceBus), Integer.decode(deviceAddress), writeBuffer, writeBuffer.length); } } // clear everything void clearDisplay() { for (int i = 0; i < buffer.length; i++) { buffer[i] = 0; } } // fill everything void fillDisplay() { for (int i = 0; i < buffer.length; i++) { buffer[i] = 0xff; } } void drawFastHLine(int x, int y, int w, int color) { boolean bSwap = false; int tmpx; switch (rotation) { case 0: // 0 degree rotation, do nothing break; case 1: // 90 degree rotation, swap x & y for rotation, then invert x bSwap = true; tmpx = x; x = y; y = tmpx; x = SSD1306_LCDWIDTH - x - 1; break; case 2: // 180 degree rotation, invert x and y - then shift y around for height. x = SSD1306_LCDWIDTH - x - 1; y = SSD1306_LCDHEIGHT - y - 1; x -= (w - 1); break; case 3: // 270 degree rotation, swap x & y for rotation, then invert y and adjust // y for w (not to become h) bSwap = true; tmpx = x; x = y; y = tmpx; y = SSD1306_LCDHEIGHT - y - 1; y -= (w - 1); break; } if (bSwap) { drawFastVLineInternal(x, y, w, color); } else { drawFastHLineInternal(x, y, w, color); } } void drawFastHLineInternal(int x, int y, int w, int color) { // Do bounds/limit checks if (y < 0 || y >= SSD1306_LCDHEIGHT) { return; } // make sure we don't try to draw below 0 if (x < 0) { w += x; x = 0; } // make sure we don't go off the edge of the display if ((x + w) > SSD1306_LCDWIDTH) { w = (SSD1306_LCDWIDTH - x); } // if our width is now negative, punt if (w <= 0) { return; } // set up the pointer for movement through the buffer // int *pBuf = buffer; int pBuf = 0; // adjust the buffer pointer for the current row pBuf += ((y / 8) * SSD1306_LCDWIDTH); // and offset x columns in pBuf += x; int mask = 1 << (y & 7); switch (color) { case WHITE: while (w > 0) { buffer[pBuf] = buffer[pBuf] | mask; pBuf++; w--; } ; break; case BLACK: mask = ~mask; while (w > 0) { buffer[pBuf] = buffer[pBuf] & mask; pBuf++; w--; } ; break; case INVERSE: while (w > 0) { buffer[pBuf] = buffer[pBuf] ^ mask; pBuf++; w--; } ; break; } } void drawFastVLine(int x, int y, int h, int color) { boolean bSwap = false; int tmpx; switch (rotation) { case 0: break; case 1: // 90 degree rotation, swap x & y for rotation, then invert x and adjust x // for h (now to become w) bSwap = true; tmpx = x; x = y; y = tmpx; x = SSD1306_LCDWIDTH - x - 1; x -= (h - 1); break; case 2: // 180 degree rotation, invert x and y - then shift y around for height. x = SSD1306_LCDWIDTH - x - 1; y = SSD1306_LCDHEIGHT - y - 1; y -= (h - 1); break; case 3: // 270 degree rotation, swap x & y for rotation, then invert y bSwap = true; tmpx = x; x = y; y = tmpx; y = SSD1306_LCDHEIGHT - y - 1; break; } if (bSwap) { drawFastHLineInternal(x, y, h, color); } else { drawFastVLineInternal(x, y, h, color); } } void drawFastVLineInternal(int x, int __y, int __h, int color) { // do nothing if we're off the left or right side of the screen if (x < 0 || x >= SSD1306_LCDWIDTH) { return; } // make sure we don't try to draw below 0 if (__y < 0) { // __y is negative, this will subtract enough from __h to account for __y // being 0 __h += __y; __y = 0; } // make sure we don't go past the height of the display if ((__y + __h) > SSD1306_LCDHEIGHT) { __h = (SSD1306_LCDHEIGHT - __y); } // if our height is now negative, punt if (__h <= 0) { return; } // this display doesn't need ints for coordinates, use local byte registers // for faster juggling int y = __y; int h = __h; int mod = y & 7; // set up the pointer for fast movement through the buffer int pBuf = 0; // adjust the buffer pointer for the current row pBuf += ((y / 8) * SSD1306_LCDWIDTH); // and offset x columns in pBuf += x; // do the first partial byte, if necessary - this requires some masking if (mod > 0) { // mask off the high n bits we want to set mod = 8 - mod; // note - lookup table results in a nearly 10% performance improvement in // fill* functions // register int mask = ~(0xFF >> (mod)); int mask = premask[mod]; // adjust the mask if we're not going to reach the end of this byte if (h < mod) { mask &= (0XFF >> (mod - h)); } switch (color) { case WHITE: buffer[pBuf] = buffer[pBuf] | mask; break; case BLACK: buffer[pBuf] = buffer[pBuf] & ~mask; break; case INVERSE: buffer[pBuf] = buffer[pBuf] ^ mask; break; } // fast exit if we're done here! if (h < mod) { return; } h -= mod; pBuf = pBuf + SSD1306_LCDWIDTH; } // write solid bytes while we can - effectively doing 8 rows at a time if (h >= 8) { 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 do { buffer[pBuf] = ~(buffer[pBuf]); // adjust the buffer forward 8 rows worth of data pBuf += SSD1306_LCDWIDTH; // 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) h -= 8; } while (h >= 8); } else { // store a local value to work with int val = (color == WHITE) ? 255 : 0; do { // write our value in buffer[pBuf] = val; // adjust the buffer forward 8 rows worth of data pBuf += SSD1306_LCDWIDTH; // 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) h -= 8; } while (h >= 8); } } // now do the final partial byte, if necessary if (h > 0) { mod = h & 7; // this time we want to mask the low bits of the byte, vs the high bits we // did above // register int mask = (1 << mod) - 1; // note - lookup table results in a nearly 10% performance improvement in // fill* functions int mask = postmask[mod]; switch (color) { case WHITE: buffer[pBuf] = buffer[pBuf] | mask; break; case BLACK: buffer[pBuf] = buffer[pBuf] & ~mask; break; case INVERSE: buffer[pBuf] = buffer[pBuf] ^ mask; break; } } } /** * This static method returns all the details of the class without it having * to be constructed. It has description, categories, dependencies, and peer * definitions. * * @return ServiceType - returns all the data * */ static public ServiceType getMetaData() { ServiceType meta = new ServiceType(OledSsd1306.class.getCanonicalName()); meta.addDescription("OLED driver using SSD1306 driver and the i2c protocol"); meta.addCategory("i2c", "control"); meta.setAvailable(true); meta.setSponsor("Mats"); return meta; } @Override public void setController(DeviceController controller) { setController(controller); } @Override public boolean isAttached() { return isControllerSet; } }