![JAR search and dependency download from the Maven repository](/logo.png)
com.ociweb.iot.grove.oled.OLED_96x96_Transducer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of foglight Show documentation
Show all versions of foglight Show documentation
FogLight is a lightweight runtime that enables makers of all ages and skill levels to create highly
performant apps for embedded devices like Raspberry Pi's.
The newest version!
package com.ociweb.iot.grove.oled;
import static com.ociweb.iot.grove.oled.OLED_96x96_DriverChip.*;
import com.ociweb.gl.api.transducer.StartupListenerTransducer;
import com.ociweb.iot.grove.oled.OLED_96x96_Consts;
import com.ociweb.iot.maker.FogCommandChannel;
import com.ociweb.iot.maker.image.FogBitmap;
import com.ociweb.iot.maker.image.FogBitmapLayout;
import com.ociweb.iot.maker.image.FogColorSpace;
import com.ociweb.pronghorn.iot.schema.I2CCommandSchema;
import com.ociweb.pronghorn.pipe.DataOutputBlobWriter;
import com.ociweb.iot.maker.IODeviceTransducer;
public class OLED_96x96_Transducer extends BinaryOLED implements IODeviceTransducer, StartupListenerTransducer{
private int lowPixelLevel = 0x0F;
private int highPixelLevel = 0xF0;
private int iteration = 0;
private OLED_96x96_DriverChip chip;
private boolean clearScreenUponStartup = true;
public OLED_96x96_Transducer(FogCommandChannel ch){
//A nibble determines pixel. A byte is therefore two horizontally adjascent pixels.
//96x96 divided 2. Since each pixel takes a nibble to send
super(ch, new int[4608], new int[32], SSD1327_Consts.ADDRESS);
ch.ensureI2CWriting(100, BATCH_SIZE);
this.chip = SSD1327;
}
@Override
public FogBitmapLayout newBmpLayout() {
FogBitmapLayout bmpLayout = new FogBitmapLayout(FogColorSpace.gray);
bmpLayout.setComponentDepth((byte) 4);
bmpLayout.setWidth(OLED_96x96_Consts.COL_COUNT);
bmpLayout.setHeight(OLED_96x96_Consts.ROW_COUNT);
return bmpLayout;
}
public boolean display(FogBitmap bmp) {
return false;
}
@Deprecated
public void setIteration(int iteration){
this.iteration = iteration;
}
public void setTextBrightness(int brightness){
lowPixelLevel = (brightness) & 0x0F;
highPixelLevel = (brightness << 4) & 0xF0;
}
//TODO: FIGURE OUT WHAT CHIP
private OLED_96x96_DriverChip determineChip(){
if(iteration % 2 ==0){
chip = SSD1327;
}
else {
chip = SH1107G;
}
System.out.println(chip);
return SH1107G;
}
@Deprecated
public void setChip(int i){
if (i == 0){
chip = SH1107G;
}
else {
chip = SSD1327;
}
}
/** Sets contrast level for the screen.
* @param contrast
* @return true if the channel was ready for the i2c commands.
*/
@Override
public boolean setContrast(int contrast){
cmd_out[0] = SSD1327_Consts.SET_CONTRAST_LEVEL_CMD;
cmd_out[1] = contrast & 0xFF;
return sendCommands(0,2);
}
/** Sets the column aand row address of the text printing function.
* @param row
* @param col
* @return true if the channel was ready for the i2c commands.
*/
@Override
public boolean setTextRowCol(int row, int col){
switch (chip){
case SSD1327:
cmd_out[0] = SSD1327_Consts.SET_COL_ADDRESS;
cmd_out[1] = 0x08 + (col*4);
cmd_out[2] = 0x37; //end column
cmd_out[3] = SSD1327_Consts.SET_ROW_ADDRESS;
cmd_out[4] = 0x00 + (row * 8);
cmd_out[5] = 0x07 + (row * 8);
return sendCommands(0,6);
case SH1107G:
int lowCol = (col % 2 ==0) ? 0x08: 0x00;
cmd_out[0] = (SH1107G_Consts.SET_ROW_BASE_BYTE+row);
cmd_out[1] = (lowCol);
cmd_out[2] = (0x11+(col/2));
return sendCommands(0,3);
default:
return false;
}
}
/**
* clears the screen by prints spaces to the entire screen.
*@return true if the channel was ready for the i2c commands.
*/
@Override
public boolean clear(){
int index = 0;
switch (chip){
case SSD1327:
setVerticalMode();
for (int row = 0; row < OLED_96x96_Consts.ROW_COUNT / 8; row++ ){
setTextRowCol(row,0);
if (!printCharSequence(" ")){ //12 spaces is one empty row
return false;
}
}
return true;
case SH1107G:
for(int j=0; j<128;j++){
data_out[j] = 0x00; //make an empty array to be sent repeatedly
}
for(int i=0; i<16;i++){
cmd_out[0] = SH1107G_Consts.SET_ROW_BASE_BYTE + i;
cmd_out[1] = 0x00;
cmd_out[2] = 0x10;
if (! sendCommands(0,3) || !sendData(0,128)){
return false;
}
}
return true; //2048 = 16 * 128
default:
return false;
}
}
@Override
protected boolean init() {
//determinChip(); TODO: this would be the place to call determineChip once implemented.
System.out.println("Chipset chosen: " + chip);
return sendCommands(0, generateInitCommands() ); //generateInitCommands() returns the length of the commands generated
}
private int generateInitCommands(){
switch (chip) {
case SSD1327:
cmd_out[0] =SSD1327_Consts.MCU;
cmd_out[1] =SSD1327_Consts.UNLOCK_CMD_ENTERING;
cmd_out[2] = SSD1327_Consts.DISPLAY_OFF;
cmd_out[3] = SSD1327_Consts.SET_MULTIPLEX_RATIO;
cmd_out[4] = 0x5F;
cmd_out[5] = SSD1327_Consts.SET_DISPLAY_START_LINE;
cmd_out[6] = 0x00;
cmd_out[7] = SSD1327_Consts.SET_DISPLAY_OFFSET;
cmd_out[8] = 0x60;
cmd_out[9] = SSD1327_Consts.REMAP;
cmd_out[10] = SSD1327_Consts.VERTICAL;
cmd_out[11] = SSD1327_Consts.SET_VDD_INTERNAL;
cmd_out[12] = 0x01;
cmd_out[13] = SSD1327_Consts.SET_CONTRAST_LEVEL_CMD;
cmd_out[14] = 0x53;
cmd_out[15] = SSD1327_Consts.SET_PHASE_LENGTH;
cmd_out[16] = 0x51;
cmd_out[17] = SSD1327_Consts.SET_CLOCK_DIV_RATIO;
cmd_out[18] = 0x01;
cmd_out[19] = 0xB9;
cmd_out[20] = SSD1327_Consts.SET_PRECHARGE_VOLTAGE_AND_VCOMH;
cmd_out[21] = 0x08;
cmd_out[22] = SSD1327_Consts.SET_VCOMH;
cmd_out[23] = 0x07;
cmd_out[24] = SSD1327_Consts.SET_SECOND_PRECHARGE_PERIOD;
cmd_out[25] = 0x01;
cmd_out[26] = SSD1327_Consts.ENABLE_SECOND_PRECHARGE_AND_INTERNAL_VSL;
cmd_out[27] = 0x62;
cmd_out[28] = SSD1327_Consts.NORMAL_DISPLAY;
cmd_out[29] = SSD1327_Consts.DEACTIVATE_SCROLL_CMD;
cmd_out[30] = SSD1327_Consts.DISPLAY_ON;
return 31;
case SH1107G:
cmd_out[0] = SH1107G_Consts.DISPLAY_OFF;
cmd_out[1] = SH1107G_Consts.SET_D_CLOCK;
cmd_out[2] = 0x50; //100Hz
cmd_out[3] = SH1107G_Consts.SET_ROW;
cmd_out[4] = SH1107G_Consts.SET_CONTRAST;
cmd_out[5] = 0x80;
cmd_out[6] = SH1107G_Consts.REMAP_SGMT;
cmd_out[7] = SH1107G_Consts.ENTIRE_DISPLAY_ON;
cmd_out[8] = SH1107G_Consts.NORMAL_DISPLAY;
cmd_out[9] = SH1107G_Consts.SET_EXT_VCC;
cmd_out[10] = 0x80;
cmd_out[11] = SH1107G_Consts.SET_COMMON_SCAN_DIR;
cmd_out[12] = SH1107G_Consts.SET_PHASE_LENGTH;
cmd_out[13] = 0x1F;
cmd_out[14] = SH1107G_Consts.SET_VCOMH_VOLTAGE;
cmd_out[15] = 0x27;
cmd_out[16] = SH1107G_Consts.DISPLAY_ON;
cmd_out[17] = SH1107G_Consts.SET_ROW_BASE_BYTE;
cmd_out[18] = 0x00;
cmd_out[19] = 0x11;
return 20;
default:
return 0;
}
}
/**
* Clears the screen while the screen is off and turns the screen backs on afterwards.
* @return true if the channel was ready for the i2c commands.
*/
@Override
public boolean cleanClear() {
return displayOff() && clear() && displayOn();
}
/**
* Turns screen on.
* @return true if the channel was ready for the i2c commands.
*/
@Override
public boolean displayOn() {
return sendCommand(SSD1327_Consts.DISPLAY_ON);
}
/**
* Turns screen off.
* @return true if the channel was ready for the i2c commands.
*/
@Override
public boolean displayOff() {
return sendCommand(SSD1327_Consts.DISPLAY_OFF);
}
/**
* Prints the charSequence at the current textRowCol position.
* @param s is the char sequence to be printed. A String can be supplied since String is a subclass of CharSequence.
* @return true if the channel was ready for the i2c commands.
*/
@Override
public boolean printCharSequence(CharSequence s) {
encodeCharSequence(s);
//TODO: decide if display needs to be in vertical mode
if (chip == SSD1327 && !setVerticalMode()){
//setVerticalMode would only execute if the chip is SSD1327
return false;
}
int charSpace = 0;
switch(chip){
case SSD1327:
charSpace = 32;
break;
case SH1107G:
charSpace = 8;
break;
}
return sendData(0, s.length()* charSpace);
}
/**
* Prints the charSequence at the specified textRowCol position.
* @param s is the char sequence to be printed. A String can be supplied since String is a subclass of CharSequence.
* @param row
* @param col
* @return true if the channel was ready for the i2c commands.
*/
@Override
public boolean printCharSequenceAt(CharSequence s, int row, int col) {
return setTextRowCol(0,0) && printCharSequence(s);
}
/**
* Encodes charSequence as the proper bytes to be send into the data_out array. With no index supplied, the method
* starts filling up the bytes at index 0 of the data_out array.
* Impl: calls {@link #encodeCharSequence(CharSequence, int)} with index defaulted to 0.
* @param s
*/
private void encodeCharSequence(CharSequence s){
encodeCharSequence(s,0);
}
private void encodeCharSequence(CharSequence s, int startingIndex){
int charIndex = 0;
int charSpace = 0;
switch(chip){
case SSD1327:
charSpace = 32;
break;
case SH1107G:
charSpace = 8;
break;
}
for (int i = startingIndex; i < startingIndex + s.length(); i++){
encodeChar(s.charAt(charIndex++), (i * charSpace));
//each char printed takes up 32 bytes of transmission (8x8 chars) where each pixel take up a nibble.
}
}
private void encodeChar(char c, int startingIndex){
if(c < 32 || c > 127){
c=' ';
}
switch(chip){
case SSD1327:
for (int i =0; i < 8; i += 2){
for(char j=0;j<8;j++)
{
char newC = 0x00;
//"Character is constructed two pixel at a time using vertical mode from the default 8x8 font"-Seeed C++ l API
newC |= (highBitAt(OLED_96x96_Consts.FONT[c-32][i],j))? highPixelLevel:0x00;
newC |= (highBitAt(OLED_96x96_Consts.FONT[c-32][i+1],j))? lowPixelLevel:0x00;
data_out[startingIndex++] = newC;
}
}
return;
case SH1107G:
for (int i = 0; i < 8; i ++){
data_out[startingIndex++] = OLED_96x96_Consts.FONT[c-32][i];
}
default:
return;
}
}
private boolean sendCommandsInQuickSuccession(int start, int length){
if (!ch.i2cIsReady()){
return false;
}
assert(length < OLED_96x96_Consts.BATCH_SIZE);
assert(start + length < OLED_96x96_Consts.BATCH_SIZE);
DataOutputBlobWriter i2cPayloadWriter = ch.i2cCommandOpen(i2c_address);
i2cPayloadWriter.write(SSD1327_Consts.MCU);
i2cPayloadWriter.write(SSD1327_Consts.UNLOCK_CMD_ENTERING);
for (int i = start; i < start + length; i ++){
i2cPayloadWriter.write(cmd_out[i]);
}
ch.i2cCommandClose(i2cPayloadWriter);
ch.i2cFlushBatch();
return true;
}
private boolean highBitAt(int b, int pos){
return ((b >> pos) & 0x01) == 0x01;
}
public boolean drawPixelMap(int[] map){
return true;
}
/**
* This function should be used over {@link #display(int[][], int)} with the pixelDepth set to 1 if the
* input data map is a one dimensional compact array where each bit corresponds to a pixel. If each int corresponds
* to a pixel, {@link #display(int[][], int)} should be the go-to method.
* @param map
* @return true if the channel was ready for the i2c commands.
*/
public boolean drawBitmap(int[] map) {
switch(chip){
case SSD1327:
int index = 0;
if ( !setHorizontalMode()){
return false;
}
for (int i = 0; i < map.length; i++){
for (int j = 0; j < 8; j = j+2){
int c = 0x00;
int b1 = (map[i] << j) & 0x80;
int b2 = (map[i] << (j+1)) & 0x80;
c |= (b1 > 0)? highPixelLevel:0x00;
c |= (b2 > 0)? lowPixelLevel:0x00;
data_out[index++] = c;
}
}
return sendData(0, map.length*4);
case SH1107G:
int row = 0;
int lowCol = 0x00;
int highCol = 0x11;
if (!setHorizontalMode()){
return false;
}
for (int i = 0; i < map.length; i++){
cmd_out[0] = (SH1107G_Consts.SET_ROW_BASE_BYTE + row);
cmd_out[1] = lowCol;
cmd_out[2] = highCol;
sendCommands(0,3);
int curByte = map[i];
int tmp = 0x00;
for (int j = 0; j < 8; j++){
tmp |= ((curByte >> (7 - j)) & 0x01) << j;
}
data_out[i] = tmp;
row++;
lowCol ++;
if (lowCol >= 16){
lowCol= 0x00;
highCol += 0x01;
}
}
return sendData(0, map.length);
default:
return false;
}
}
@Override
public boolean activateScroll() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean deactivateScroll() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean setUpScroll() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean inverseOn() {
return sendCommand(SSD1327_Consts.INVERSE_DISPLAY); //the two chips share the same command byte
}
@Override
public boolean inverseOff() {
return sendCommand(SSD1327_Consts.NORMAL_DISPLAY); //the two chips share the same command byte
}
@Override
public boolean display(int[][] raw_image) {
return display(raw_image, 4);
}
/**
* Displays 96x96 integer array.
* @param raw_image
* @param pixelDepth is the pixelDepth in bits of the input; this information allows the method to automatically
* convert to 4-bit pixelDepth as spec'ed by the physical hardware.
* @return true if the channel was ready for the i2c commands.
*/
@Override
public boolean display(int[][] raw_image, int pixelDepth){
switch (chip){
case SSD1327:
if (!setHorizontalMode()){
return false;
}
int index = 0;
int mask = (1 << pixelDepth) - 1;
for (int i = 0; i < OLED_96x96_Consts.ROW_COUNT; i ++){
for (int j = 0; j < OLED_96x96_Consts.COL_COUNT; j = j + 2){
int b = (raw_image[i][j] & mask) << 4;
b |= raw_image[i][j+1] & mask;
data_out[index++] = b;
}
}
return sendData(0,index);
case SH1107G:
//TODO: implement function for SH1107G chip
return false;
default:
return false;
}
}
@Override
public boolean setHorizontalMode() {
switch(chip){
case SSD1327:
cmd_out[0] = SSD1327_Consts.REMAP;
cmd_out[1] = SSD1327_Consts.HORIZONTAL;
cmd_out[2] = SSD1327_Consts.SET_ROW_ADDRESS;
cmd_out[3] = 0;
cmd_out[4] = 95;
cmd_out[5] = SSD1327_Consts.SET_COL_ADDRESS;
cmd_out[6] = 8; //the 8th column on the chip corresponds to the 0th column on the actual screen
cmd_out[7] = 47 + 8;//end at col + 47th column, again offset by 8.
return sendCommands(0,8);
case SH1107G:
cmd_out[0] = SH1107G_Consts.REMAP_SGMT;
cmd_out[1] = SH1107G_Consts.SET_HORIZONTAL;
return sendCommands(0,2);
default:
return false;
}
}
@Override
public boolean setVerticalMode() {
switch(chip){
case SSD1327:
cmd_out[0] = SSD1327_Consts.REMAP;
cmd_out[1] = SSD1327_Consts.VERTICAL;
break;
case SH1107G:
cmd_out[0] = SH1107G_Consts.REMAP_SGMT;
cmd_out[1] = SH1107G_Consts.SET_VERTICAL;
break;
default:
return false;
}
return sendCommands(0,2);
}
/*
/**
* Overrides the base implementation and sends a "DATA_MODE" identifier byte before every single data byte.
* @param data
* @param start
* @param length
* @param finalTargetIndex
* @return true if the data is sent; false otherwise.
*/
/*
@Override
protected boolean sendData(int [] data, int start, int length, int finalTargetIndex){
DataOutputBlobWriter i2cPayloadWriter = ch.i2cCommandOpen(i2c_address);
length = length / 2; //we need to send two bytes for each command
int i;
for (i = start; i < Math.min(start + length, finalTargetIndex); i++){
i2cPayloadWriter.write(DATA_MODE);
i2cPayloadWriter.write(data[i]);
}
ch.i2cCommandClose();
ch.i2cFlushBatch();
if (i == finalTargetIndex){
return true;
}
return sendData(data, i, BATCH_SIZE, finalTargetIndex); //calls itself recursively until we reach finalTargetIndex
}
*/
public void setClearScreenUponStartup(boolean clear){
clearScreenUponStartup = clear;
}
@Override
public void startup() {
logger.info("OLED_96x96 initialized and cleared.");
this.init();
if (clearScreenUponStartup){
this.clear();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy