Skip to content

Commit

Permalink
First working Elekstube IPS and RTC mod
Browse files Browse the repository at this point in the history
  • Loading branch information
Aircoookie committed May 21, 2021
1 parent 371c4e0 commit 2d58640
Show file tree
Hide file tree
Showing 23 changed files with 762 additions and 0 deletions.
34 changes: 34 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -473,3 +473,37 @@ platform_packages = ${common.platform_packages}
board_build.ldscript = ${common.ldscript_4m1m}
build_unflags = ${common.build_unflags}
build_flags = ${common.build_flags_esp8266}

# ------------------------------------------------------------------------------
# EleksTube-IPS
# ------------------------------------------------------------------------------
[env:elekstube_ips]
board = esp32dev
platform = [email protected]
upload_speed = 921600
lib_deps = ${env.lib_deps}
TFT_eSPI
build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_DISABLE_INFRARED
-D USERMOD_RTC
-D USERMOD_ELEKSTUBE_IPS
-D LEDPIN=12
-D RLYPIN=27
-D BTNPIN=34
-D WLED_DISABLE_INFRARED
-D DEFAULT_LED_COUNT=6
# Display config
-D ST7789_DRIVER
-D TFT_WIDTH=135
-D TFT_HEIGHT=240
-D CGRAM_OFFSET
-D TFT_SDA_READ
-D TFT_MOSI=23
-D TFT_SCLK=18
-D TFT_DC=25
-D TFT_RST=26
-D SPI_FREQUENCY=40000000
-D USER_SETUP_LOADED
monitor_filters = esp32_exception_decoder
lib_ignore =
ESPAsyncTCP
ESPAsyncUDP
70 changes: 70 additions & 0 deletions usermods/EleksTube_IPS/ChipSelect.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#ifndef CHIP_SELECT_H
#define CHIP_SELECT_H

#include "Hardware.h"

/*
* `digit`s are as defined in Hardware.h, 0 == seconds ones, 5 == hours tens.
*/

class ChipSelect {
private:
uint8_t digits_map;
const uint8_t all_on = 0x3F;
const uint8_t all_off = 0x00;
public:
ChipSelect() : digits_map(all_off) {}

void update() {
// Documented in README.md. Q7 and Q6 are unused. Q5 is Seconds Ones, Q0 is Hours Tens.
// Q7 is the first bit written, Q0 is the last. So we push two dummy bits, then start with
// Seconds Ones and end with Hours Tens.
// CS is Active Low, but digits_map is 1 for enable, 0 for disable. So we bit-wise NOT first.

uint8_t to_shift = (~digits_map) << 2;

digitalWrite(CSSR_LATCH_PIN, LOW);
shiftOut(CSSR_DATA_PIN, CSSR_CLOCK_PIN, LSBFIRST, to_shift);
digitalWrite(CSSR_LATCH_PIN, HIGH);
}

void begin()
{
pinMode(CSSR_LATCH_PIN, OUTPUT);
pinMode(CSSR_DATA_PIN, OUTPUT);
pinMode(CSSR_CLOCK_PIN, OUTPUT);

digitalWrite(CSSR_DATA_PIN, LOW);
digitalWrite(CSSR_CLOCK_PIN, LOW);
digitalWrite(CSSR_LATCH_PIN, LOW);
update();
}

// These speak the indexes defined in Hardware.h.
// So 0 is disabled, 1 is enabled (even though CS is active low, this gets mapped.)
// So bit 0 (LSB), is index 0, is SECONDS_ONES
// Translation to what the 74HC595 uses is done in update()
void setDigitMap(uint8_t map, bool update_=true) { digits_map = map; if (update_) update(); }
uint8_t getDigitMap() { return digits_map; }

// Helper functions
// Sets just the one digit by digit number
void setDigit(uint8_t digit, bool update_=true) { setDigitMap(0x01 << digit, update_); }
void setAll(bool update_=true) { setDigitMap(all_on, update_); }
void clear(bool update_=true) { setDigitMap(all_off, update_); }
void setSecondsOnes() { setDigit(SECONDS_ONES); }
void setSecondsTens() { setDigit(SECONDS_TENS); }
void setMinutesOnes() { setDigit(MINUTES_ONES); }
void setMinutesTens() { setDigit(MINUTES_TENS); }
void setHoursOnes() { setDigit(HOURS_ONES); }
void setHoursTens() { setDigit(HOURS_TENS); }
bool isSecondsOnes() { return (digits_map&SECONDS_ONES_MAP > 0); }
bool isSecondsTens() { return (digits_map&SECONDS_TENS_MAP > 0); }
bool isMinutesOnes() { return (digits_map&MINUTES_ONES_MAP > 0); }
bool isMinutesTens() { return (digits_map&MINUTES_TENS_MAP > 0); }
bool isHoursOnes() { return (digits_map&HOURS_ONES_MAP > 0); }
bool isHoursTens() { return (digits_map&HOURS_TENS_MAP > 0); }
};


#endif // CHIP_SELECT_H
52 changes: 52 additions & 0 deletions usermods/EleksTube_IPS/Hardware.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Define the hardware for the EleksTube IPS clock. Mostly pin definitions
*/
#ifndef ELEKSTUBEHAX_HARDWARE_H
#define ELEKSTUBEHAX_HARDWARE_H

#include <stdint.h>
#include <Arduino.h> // for HIGH and LOW

// Common indexing scheme, used to identify the digit
#define SECONDS_ONES (0)
#define SECONDS_TENS (1)
#define MINUTES_ONES (2)
#define MINUTES_TENS (3)
#define HOURS_ONES (4)
#define HOURS_TENS (5)
#define NUM_DIGITS (6)

#define SECONDS_ONES_MAP (0x01 << SECONDS_ONES)
#define SECONDS_TENS_MAP (0x01 << SECONDS_TENS)
#define MINUTES_ONES_MAP (0x01 << MINUTES_ONES)
#define MINUTES_TENS_MAP (0x01 << MINUTES_TENS)
#define HOURS_ONES_MAP (0x01 << HOURS_ONES)
#define HOURS_TENS_MAP (0x01 << HOURS_TENS)

// WS2812 (or compatible) LEDs on the back of the display modules.
#define BACKLIGHTS_PIN (12)

// Buttons, active low, externally pulled up (with actual resistors!)
#define BUTTON_LEFT_PIN (33)
#define BUTTON_MODE_PIN (32)
#define BUTTON_RIGHT_PIN (35)
#define BUTTON_POWER_PIN (34)

// I2C to DS3231 RTC.
#define RTC_SCL_PIN (22)
#define RTC_SDA_PIN (21)

// Chip Select shift register, to select the display
#define CSSR_DATA_PIN (14)
#define CSSR_CLOCK_PIN (16)
#define CSSR_LATCH_PIN (17)

// SPI to displays
// DEFINED IN User_Setup.h
// Look for: TFT_MOSI, TFT_SCLK, TFT_CS, TFT_DC, and TFT_RST

// Power for all TFT displays are grounded through a MOSFET so they can all be turned off.
// Active HIGH.
#define TFT_ENABLE_PIN (27)

#endif // ELEKSTUBEHAX_HARDWARE_H
169 changes: 169 additions & 0 deletions usermods/EleksTube_IPS/TFTs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#ifndef TFTS_H
#define TFTS_H

#include <FS.h>

#include <TFT_eSPI.h>
#include "Hardware.h"
#include "ChipSelect.h"

class TFTs : public TFT_eSPI {
private:
uint8_t digits[NUM_DIGITS];

// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(fs::File &f) {
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}

uint32_t read32(fs::File &f) {
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}

uint16_t output_buffer[TFT_HEIGHT][TFT_WIDTH];

// These BMP functions are stolen directly from the TFT_SPIFFS_BMP example in the TFT_eSPI library.
// Unfortunately, they aren't part of the library itself, so I had to copy them.
// I've modified drawBmp to buffer the whole image at once instead of doing it line-by-line.

//// BEGIN STOLEN CODE

bool drawBmp(const char *filename, int16_t x, int16_t y) {
// Nothing to do.
if ((x >= width()) || (y >= height())) return(true);

fs::File bmpFS;

// Open requested file on SD card
bmpFS = WLED_FS.open(filename, "r");

if (!bmpFS)
{
Serial.println(F("File not found"));
return(false);
}

uint32_t seekOffset;
int16_t w, h, row;
uint8_t r, g, b;

uint16_t magic = read16(bmpFS);
if (magic == 0xFFFF) {
Serial.println(F("BMP not found!"));
bmpFS.close();
return(false);
}

if (magic != 0x4D42) {
Serial.print(F("File not a BMP. Magic: "));
Serial.println(magic);
bmpFS.close();
return(false);
}

read32(bmpFS);
read32(bmpFS);
seekOffset = read32(bmpFS);
read32(bmpFS);
w = read32(bmpFS);
h = read32(bmpFS);

if ((read16(bmpFS) != 1) || (read16(bmpFS) != 24) || (read32(bmpFS) != 0)) {
Serial.println(F("BMP format not recognized."));
bmpFS.close();
return(false);
}

bool oldSwapBytes = getSwapBytes();
setSwapBytes(true);
bmpFS.seek(seekOffset);

uint16_t padding = (4 - ((w * 3) & 3)) & 3;
uint8_t lineBuffer[w * 3 + padding];

// row is decremented as the BMP image is drawn bottom up
for (row = h-1; row >= 0; row--) {
if (row & 0b00000111 == 7) strip.service(); //still refresh backlight to mitigate stutter every few rows
bmpFS.read(lineBuffer, sizeof(lineBuffer));
uint8_t* bptr = lineBuffer;

// Convert 24 to 16 bit colours while copying to output buffer.
for (uint16_t col = 0; col < w; col++)
{
b = *bptr++;
g = *bptr++;
r = *bptr++;
output_buffer[row][col] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
}

pushImage(x, y, w, h, (uint16_t *)output_buffer);
setSwapBytes(oldSwapBytes);

bmpFS.close();
return(true);
}

public:
TFTs() : TFT_eSPI(), chip_select()
{ for (uint8_t digit=0; digit < NUM_DIGITS; digit++) digits[digit] = 0; }

// no == Do not send to TFT. yes == Send to TFT if changed. force == Send to TFT.
enum show_t { no, yes, force };
// A digit of 0xFF means blank the screen.
const static uint8_t blanked = 255;

void begin() {
pinMode(TFT_ENABLE_PIN, OUTPUT);
digitalWrite(TFT_ENABLE_PIN, HIGH); //enable displays on boot

// Start with all displays selected.
chip_select.begin();
chip_select.setAll();

// Initialize the super class.
init();
}

void showDigit(uint8_t digit) {
chip_select.setDigit(digit);

if (digits[digit] == blanked) {
fillScreen(TFT_BLACK);
}
else {
// Filenames are no bigger than "255.bmp\0"
char file_name[10];
sprintf(file_name, "/%d.bmp", digits[digit]);
drawBmp(file_name, 0, 0);
}
}

void setDigit(uint8_t digit, uint8_t value, show_t show=yes) {
uint8_t old_value = digits[digit];
digits[digit] = value;

if (show != no && (old_value != value || show == force)) {
showDigit(digit);
}
}
uint8_t getDigit(uint8_t digit) { return digits[digit]; }

void showAllDigits() { for (uint8_t digit=0; digit < NUM_DIGITS; digit++) showDigit(digit); }

// Making chip_select public so we don't have to proxy all methods, and the caller can just use it directly.
ChipSelect chip_select;
};

#endif // TFTS_H
47 changes: 47 additions & 0 deletions usermods/EleksTube_IPS/User_Setup.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* This is intended to over-ride `User_Setup.h` that comes with the TFT_eSPI library.
* I hate having to modify the library code.
*/

// ST7789 135 x 240 display with no chip select line

#define ST7789_DRIVER // Configure all registers

#define TFT_WIDTH 135
#define TFT_HEIGHT 240

#define CGRAM_OFFSET // Library will add offsets required

//#define TFT_RGB_ORDER TFT_RGB // Colour order Red-Green-Blue
//#define TFT_RGB_ORDER TFT_BGR // Colour order Blue-Green-Red

//#define TFT_INVERSION_ON
//#define TFT_INVERSION_OFF

// EleksTube IPS
#define TFT_SDA_READ // Read and write on the MOSI/SDA pin, no separate MISO pin
#define TFT_MOSI 23
#define TFT_SCLK 18
//#define TFT_CS -1 // Not connected
#define TFT_DC 25 // Data Command, aka Register Select or RS
#define TFT_RST 26 // Connect reset to ensure display initialises

#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
//#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH, 96 characters
//#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH, 96 characters
//#define LOAD_FONT6 // Font 6. Large 48 pixel font, needs ~2666 bytes in FLASH, only characters 1234567890:-.apm
//#define LOAD_FONT7 // Font 7. 7 segment 48 pixel font, needs ~2438 bytes in FLASH, only characters 1234567890:.
//#define LOAD_FONT8 // Font 8. Large 75 pixel font needs ~3256 bytes in FLASH, only characters 1234567890:-.
//#define LOAD_FONT8N // Font 8. Alternative to Font 8 above, slightly narrower, so 3 digits fit a 160 pixel TFT
//#define LOAD_GFXFF // FreeFonts. Include access to the 48 Adafruit_GFX free fonts FF1 to FF48 and custom fonts

//#define SMOOTH_FONT


//#define SPI_FREQUENCY 27000000
#define SPI_FREQUENCY 40000000

/*
* To make the Library not over-write all this:
*/
#define USER_SETUP_LOADED
Binary file added usermods/EleksTube_IPS/bmp/0.bmp
Binary file not shown.
Binary file added usermods/EleksTube_IPS/bmp/1.bmp
Binary file not shown.
Binary file added usermods/EleksTube_IPS/bmp/2.bmp
Binary file not shown.
Binary file added usermods/EleksTube_IPS/bmp/3.bmp
Binary file not shown.
Binary file added usermods/EleksTube_IPS/bmp/4.bmp
Binary file not shown.
Binary file added usermods/EleksTube_IPS/bmp/5.bmp
Binary file not shown.
Binary file added usermods/EleksTube_IPS/bmp/6.bmp
Binary file not shown.
Binary file added usermods/EleksTube_IPS/bmp/7.bmp
Binary file not shown.
Binary file added usermods/EleksTube_IPS/bmp/8.bmp
Binary file not shown.
Binary file added usermods/EleksTube_IPS/bmp/9.bmp
Binary file not shown.
Loading

0 comments on commit 2d58640

Please sign in to comment.