From 3fabebf0abeb0d04ae43db56bb15af19eadb3fd0 Mon Sep 17 00:00:00 2001 From: Adrian Houghton Date: Fri, 7 Jun 2019 17:50:05 +0200 Subject: [PATCH 1/4] Update lcd.go Added configurable strobe delays. --- lcd.go | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/lcd.go b/lcd.go index 5f13641..15a3b64 100644 --- a/lcd.go +++ b/lcd.go @@ -64,13 +64,19 @@ const ( ) type Lcd struct { - i2c *i2c.I2C - backlight bool - lcdType LcdType + i2c *i2c.I2C + backlight bool + lcdType LcdType + writeStrobeDelay uint16 + resetStrobeDelay uint16 } func NewLcd(i2c *i2c.I2C, lcdType LcdType) (*Lcd, error) { - this := &Lcd{i2c: i2c, backlight: false, lcdType: lcdType} + this := &Lcd{i2c: i2c, + backlight: false, + lcdType: lcdType, + writeStrobeDelay: 200, + resetStrobeDelay: 30} initByteSeq := []byte{ 0x03, 0x03, 0x03, // base initialization 0x02, // setting up 4-bit transfer mode @@ -92,6 +98,7 @@ func NewLcd(i2c *i2c.I2C, lcdType LcdType) (*Lcd, error) { if err != nil { return nil, err } + return this, nil } @@ -117,8 +124,8 @@ func (this *Lcd) writeDataWithStrobe(data byte) error { } seq := []rawData{ {data, 0}, // send data - {data | PIN_EN, 200 * time.Microsecond}, // set strobe - {data, 30 * time.Microsecond}, // reset strobe + {data | PIN_EN, time.Duration(this.writeStrobeDelay) * time.Microsecond}, // set strobe + {data, time.Duration(this.resetStrobeDelay) * time.Microsecond}, // reset strobe } return this.writeRawDataSeq(seq) } @@ -241,6 +248,7 @@ func (this *Lcd) TestWriteCGRam() error { func (this *Lcd) BacklightOn() error { this.backlight = true err := this.writeByte(0x00, 0) + time.Sleep(10 * time.Millisecond) //Slight delay seems to help stablize on RPi3 if err != nil { return err } @@ -250,6 +258,7 @@ func (this *Lcd) BacklightOn() error { func (this *Lcd) BacklightOff() error { this.backlight = false err := this.writeByte(0x00, 0) + time.Sleep(10 * time.Millisecond) //Slight delay seems to help stablize on RPi3 if err != nil { return err } @@ -258,12 +267,13 @@ func (this *Lcd) BacklightOff() error { func (this *Lcd) Clear() error { err := this.writeByte(CMD_Clear_Display, 0) + time.Sleep(10 * time.Millisecond) //Slight delay seems to help stablize on RPi3 return err } func (this *Lcd) Home() error { err := this.writeByte(CMD_Return_Home, 0) - time.Sleep(3 * time.Millisecond) + time.Sleep(10 * time.Millisecond) //Slight delay seems to help stablize on RPi3 return err } @@ -308,3 +318,16 @@ func (this *Lcd) Command(cmd byte) error { err := this.writeByte(cmd, 0) return err } + +// GetStrobeDelays returns the SET and RESET strobe delays. +func (this *Lcd) GetStrobeDelays() (writeDelay, resetDelay uint16) { + return this.writeStrobeDelay, this.resetStrobeDelay +} + +// SetStrobeDelays sets the SET and RESET strobe delays. +// These will be multiplied by time.microsecond. +// For RPi 3, 240 and 30 seem stable values to use. +func (this *Lcd) SetStrobeDelays(writeDelay, resetDelay uint16) { + this.writeStrobeDelay = writeDelay + this.resetStrobeDelay = resetDelay +} From 11bda63a7a7f90ff2ad153ac034b2bd3c3ade149 Mon Sep 17 00:00:00 2001 From: Adrian Houghton Date: Fri, 7 Jun 2019 20:25:50 +0200 Subject: [PATCH 2/4] Update lcd.go Updated LCD initialize routine to have correct delays according to datasheet. --- lcd.go | 50 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/lcd.go b/lcd.go index 15a3b64..5404d79 100644 --- a/lcd.go +++ b/lcd.go @@ -73,27 +73,61 @@ type Lcd struct { func NewLcd(i2c *i2c.I2C, lcdType LcdType) (*Lcd, error) { this := &Lcd{i2c: i2c, - backlight: false, - lcdType: lcdType, - writeStrobeDelay: 200, - resetStrobeDelay: 30} + backlight: false, + lcdType: lcdType, + writeStrobeDelay: 200, + resetStrobeDelay: 30} + + // Wait is required during initialization steps. + // https://www.sparkfun.com/datasheets/LCD/HD44780.pdf (page 45) + + // Step 1 -> Must be sent according to datasheet with minimum delay afterwards + var err = this.writeByte(0x03, 0) + if err != nil { + return nil, err + } + time.Sleep(4100 * time.Microsecond) + + // Step 2 -> Must be sent according to datasheet with minimum delay afterwards + err = this.writeByte(0x03, 0) + if err != nil { + return nil, err + } + time.Sleep(150 * time.Microsecond) + + // Step 3 -> Must be sent according to datasheet with minimum delay afterwards + err = this.writeByte(0x03, 0) + if err != nil { + return nil, err + } + time.Sleep(100 * time.Microsecond) + + // Step 4 -> Setting up 4-bit transfer mode (delay not spec'ed, but lets be safe) + err = this.writeByte(0x03, 0) + if err != nil { + return nil, err + } + time.Sleep(100 * time.Microsecond) + + // Step 5 -> Setting up display options (delay not spec'ed, but lets be safe) initByteSeq := []byte{ - 0x03, 0x03, 0x03, // base initialization - 0x02, // setting up 4-bit transfer mode CMD_Function_Set | OPT_2_Lines | OPT_5x8_Dots | OPT_4Bit_Mode, CMD_Display_Control | OPT_Enable_Display, CMD_Entry_Mode | OPT_Increment, } for _, b := range initByteSeq { - err := this.writeByte(b, 0) + err = this.writeByte(b, 0) + time.Sleep(100 * time.Microsecond) if err != nil { return nil, err } } - err := this.Clear() + + err = this.Clear() if err != nil { return nil, err } + err = this.Home() if err != nil { return nil, err From a4e4ad3b27209cb9b71ac66ade24048f545debae Mon Sep 17 00:00:00 2001 From: AdrianH Date: Sun, 21 Jul 2019 11:56:55 +0200 Subject: [PATCH 3/4] * Improved the INIT code to be more stable and follow the datasheet guidelines. * Added Fill() function. * Added Shutdown() function. * Changes to examples. --- examples/example1/example1.go | 14 +- examples/example2/example2.go | 0 examples/example3/example3.go | 15 +- examples/example4/example4.go | 4 +- examples/helloworld/main.go | 4 +- lcd.go | 250 +++++++++++++++++++++++++++------- 6 files changed, 224 insertions(+), 63 deletions(-) mode change 100644 => 100755 examples/example2/example2.go mode change 100644 => 100755 examples/helloworld/main.go diff --git a/examples/example1/example1.go b/examples/example1/example1.go index fa9ae15..ae6219c 100644 --- a/examples/example1/example1.go +++ b/examples/example1/example1.go @@ -1,12 +1,12 @@ package main import ( - "bytes" + //"bytes" "fmt" "log" "time" - device "github.com/d2r2/go-hd44780" + device "github.com/adrianh-za/go-hd44780" "github.com/d2r2/go-i2c" ) @@ -17,7 +17,7 @@ func checkError(err error) { } func main() { - i2c, err := i2c.NewI2C(0x27, 2) + i2c, err := i2c.NewI2C(0x27, 1) checkError(err) defer i2c.Close() lcd, err := device.NewLcd(i2c, device.LCD_16x2) @@ -32,7 +32,7 @@ func main() { // checkError(err) // err = lcd.ShowMessage("R2D2, where are you?", device.SHOW_LINE_4) // checkError(err) - time.Sleep(5 * time.Second) + /*time.Sleep(5 * time.Second) for i := 0; i <= 12; i++ { var buf bytes.Buffer for j := 0; j <= 19; j++ { @@ -53,14 +53,14 @@ func main() { err = lcd.ShowMessage(buf.String(), device.SHOW_LINE_1) checkError(err) time.Sleep(1 * time.Second) - } + }*/ lcd.Clear() for { lcd.Home() t := time.Now() - lcd.SetPosition(1, 0) + lcd.SetPosition(0, 0) fmt.Fprint(lcd, t.Format("Monday Jan 2")) - lcd.SetPosition(2, 1) + lcd.SetPosition(1, 1) fmt.Fprint(lcd, t.Format("15:04:05 2006")) // lcd.SetPosition(4, 0) // fmt.Fprint(lcd, "i2c, VGA, and Go") diff --git a/examples/example2/example2.go b/examples/example2/example2.go old mode 100644 new mode 100755 diff --git a/examples/example3/example3.go b/examples/example3/example3.go index 00894f6..14c09e4 100644 --- a/examples/example3/example3.go +++ b/examples/example3/example3.go @@ -5,7 +5,7 @@ import ( "strings" "time" - device "github.com/d2r2/go-hd44780" + device "github.com/adrianh-za/go-hd44780" "github.com/d2r2/go-i2c" ) @@ -16,10 +16,11 @@ func checkError(err error) { } func main() { - i2c, err := i2c.NewI2C(0x27, 2) + i2c, err := i2c.NewI2C(0x27, 1) checkError(err) defer i2c.Close() lcd, err := device.NewLcd(i2c, device.LCD_20x4) + //lcd.SetStrobeDelays(400, 50) checkError(err) err = lcd.BacklightOn() checkError(err) @@ -44,11 +45,13 @@ func main() { err = lcd.ShowMessage(strings.Repeat(" ", 20), lines[j]) checkError(err) } - err = lcd.BacklightOff() - checkError(err) time.Sleep(2 * time.Second) - err = lcd.BacklightOn() - checkError(err) + //err = lcd.BacklightOff() + //checkError(err) + //time.Sleep(2 * time.Second) + //err = lcd.BacklightOn() + //checkError(err) + i++ } } diff --git a/examples/example4/example4.go b/examples/example4/example4.go index e480051..e8672e8 100644 --- a/examples/example4/example4.go +++ b/examples/example4/example4.go @@ -4,7 +4,7 @@ import ( "log" "time" - device "github.com/d2r2/go-hd44780" + device "github.com/adrianh-za/go-hd44780" "github.com/d2r2/go-i2c" ) @@ -15,7 +15,7 @@ func checkError(err error) { } func main() { - i2c, err := i2c.NewI2C(0x27, 2) + i2c, err := i2c.NewI2C(0x27, 1) checkError(err) defer i2c.Close() lcd, err := device.NewLcd(i2c, device.LCD_20x4) diff --git a/examples/helloworld/main.go b/examples/helloworld/main.go old mode 100644 new mode 100755 index 0656cd9..3d16cce --- a/examples/helloworld/main.go +++ b/examples/helloworld/main.go @@ -5,7 +5,7 @@ import ( "log" "time" - device "github.com/d2r2/go-hd44780" + device "github.com/adrianh-za/go-hd44780" i2c "github.com/d2r2/go-i2c" ) @@ -16,7 +16,7 @@ func check(err error) { } func main() { - i2c, err := i2c.NewI2C(0x27, 2) + i2c, err := i2c.NewI2C(0x27, 1) check(err) defer i2c.Close() lcd, err := device.NewLcd(i2c, device.LCD_16x2) diff --git a/lcd.go b/lcd.go index 5404d79..8d7ad74 100644 --- a/lcd.go +++ b/lcd.go @@ -14,29 +14,39 @@ const ( CMD_Return_Home = 0x02 CMD_Entry_Mode = 0x04 CMD_Display_Control = 0x08 - CMD_Cursor_Display_Shift = 0x10 + CMD_Cursor_Shift = 0x10 CMD_Function_Set = 0x20 CMD_CGRAM_Set = 0x40 CMD_DDRAM_Set = 0x80 - // Options - OPT_Increment = 0x02 // CMD_Entry_Mode - OPT_Decrement = 0x00 - // OPT_Display_Shift = 0x01 // CMD_Entry_Mode - OPT_Enable_Display = 0x04 // CMD_Display_Control - OPT_Enable_Cursor = 0x02 // CMD_Display_Control - OPT_Enable_Blink = 0x01 // CMD_Display_Control - OPT_Display_Shift = 0x08 // CMD_Cursor_Display_Shift - OPT_Shift_Right = 0x04 // CMD_Cursor_Display_Shift 0 = Left + // Flags for display entry mode (CMD_Entry_Mode) + OPT_EntryLeft = 0x02 + OPT_EntryRight = 0x00 + OPT_Increment = 0x01 + OPT_Decrement = 0x00 + + // Flags for display control (CMD_Display_Control) + OPT_Enable_Display = 0x04 + OPT_Disable_Display = 0x00 + OPT_Enable_Cursor = 0x02 + OPT_Disable_Cursor = 0x00 + OPT_Enable_Blink = 0x01 + OPT_Disable_Blink = 0x00 + + // Flags for display/cursor move () + OPT_Display_Move = 0x08 + OPT_Cursor_Move = 0x00 + OPT_Move_Right = 0x04 + OPT_Move_Left = 0x00 + + // Flags for function set (CMD_Function_Set) OPT_8Bit_Mode = 0x10 OPT_4Bit_Mode = 0x00 - OPT_2_Lines = 0x08 // CMD_Function_Set 0 = 1 line + OPT_2_Lines = 0x08 OPT_1_Lines = 0x00 - OPT_5x10_Dots = 0x04 // CMD_Function_Set 0 = 5x7 dots + OPT_5x10_Dots = 0x04 OPT_5x8_Dots = 0x00 -) -const ( PIN_BACKLIGHT byte = 0x08 PIN_EN byte = 0x04 // Enable bit PIN_RW byte = 0x02 // Read/Write bit @@ -69,6 +79,10 @@ type Lcd struct { lcdType LcdType writeStrobeDelay uint16 resetStrobeDelay uint16 + active bool + displayFunction byte + displayControl byte + displayMode byte } func NewLcd(i2c *i2c.I2C, lcdType LcdType) (*Lcd, error) { @@ -76,58 +90,81 @@ func NewLcd(i2c *i2c.I2C, lcdType LcdType) (*Lcd, error) { backlight: false, lcdType: lcdType, writeStrobeDelay: 200, - resetStrobeDelay: 30} + resetStrobeDelay: 30, + active: true, + displayFunction: 0x00, + displayControl: 0x00, + displayMode: 0x00, + } - // Wait is required during initialization steps. + // Wait is required during initialization steps. Various info below about delays. // https://www.sparkfun.com/datasheets/LCD/HD44780.pdf (page 45) - - // Step 1 -> Must be sent according to datasheet with minimum delay afterwards - var err = this.writeByte(0x03, 0) + // https://github.com/mrmorphic/hwio/blob/master/devices/hd44780/hd44780_i2c.go + // https://github.com/duinoWitchery/hd44780/blob/master/hd44780.cpp (read the comments) + + // Initial delay as per datasheet (need at least 40ms after power rises above 2.7V before sending commands.) + time.Sleep(100 * time.Millisecond) // Wait 100ms vs 40ms + + // Step 1 -> Base initialization sent with safe minimum delay afterwards + var err = this.writeByte(0x03, 0) if err != nil { return nil, err } - time.Sleep(4100 * time.Microsecond) + time.Sleep(5 * time.Millisecond) // Wait 5ms vs 4.1ms - // Step 2 -> Must be sent according to datasheet with minimum delay afterwards + // Step 2 -> Base initialization sent with safe minimum delay afterwards err = this.writeByte(0x03, 0) if err != nil { return nil, err } - time.Sleep(150 * time.Microsecond) + time.Sleep(1 * time.Millisecond) // Wait 1ms vs 100us - // Step 3 -> Must be sent according to datasheet with minimum delay afterwards + // Step 3 -> Base initialization sent with safe minimum delay afterwards err = this.writeByte(0x03, 0) if err != nil { return nil, err } - time.Sleep(100 * time.Microsecond) + time.Sleep(1 * time.Millisecond) // Wait 1ms vs 100us - // Step 4 -> Setting up 4-bit transfer mode (delay not spec'ed, but lets be safe) - err = this.writeByte(0x03, 0) + // Step 4 -> 4-bit transfer mode sent with safe minimum delay afterwards + err = this.writeByte(0x02, 0) if err != nil { return nil, err } - time.Sleep(100 * time.Microsecond) + time.Sleep(1 * time.Millisecond) // Wait 1ms vs 100us - // Step 5 -> Setting up display options (delay not spec'ed, but lets be safe) - initByteSeq := []byte{ - CMD_Function_Set | OPT_2_Lines | OPT_5x8_Dots | OPT_4Bit_Mode, - CMD_Display_Control | OPT_Enable_Display, - CMD_Entry_Mode | OPT_Increment, + // Step 5a -> Execute FUNCTIONSET command + this.displayFunction = OPT_2_Lines | OPT_5x8_Dots | OPT_4Bit_Mode + err = this.writeByte(CMD_Function_Set | this.displayFunction, 0) + time.Sleep(1 * time.Millisecond) // Wait 1ms to be safe + if err != nil { + return nil, err } - for _, b := range initByteSeq { - err = this.writeByte(b, 0) - time.Sleep(100 * time.Microsecond) - if err != nil { - return nil, err - } + + // Step 5b -> Execute DISPLAYCONTROL command + this.displayControl = OPT_Enable_Display | OPT_Disable_Cursor | OPT_Disable_Blink + err = this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(1 * time.Millisecond) // Wait 1ms to be safe + if err != nil { + return nil, err } + // Step 5c -> Execute ENTRYMODE command + this.displayMode = OPT_EntryLeft + err = this.writeByte(CMD_Entry_Mode | this.displayMode, 0) + time.Sleep(1 * time.Millisecond) // Wait 1ms to be safe + if err != nil { + return nil, err + + } + + // Clear the display err = this.Clear() if err != nil { return nil, err } + // Send cursor to home err = this.Home() if err != nil { return nil, err @@ -237,6 +274,11 @@ func (this *Lcd) splitText(text string, options ShowOptions) []string { } func (this *Lcd) ShowMessage(text string, options ShowOptions) error { + //Not active, so don't try do anything + if (!this.active) { + return nil + } + lines := this.splitText(text, options) log.Debug("Output: %v\n", lines) startLine, endLine := this.getLineRange(options) @@ -282,7 +324,7 @@ func (this *Lcd) TestWriteCGRam() error { func (this *Lcd) BacklightOn() error { this.backlight = true err := this.writeByte(0x00, 0) - time.Sleep(10 * time.Millisecond) //Slight delay seems to help stablize on RPi3 + time.Sleep(100 * time.Millisecond) // Used delay as specified in https://github.com/duinoWitchery/hd44780/blob/master/hd44780.cpp (Line 885) if err != nil { return err } @@ -292,7 +334,7 @@ func (this *Lcd) BacklightOn() error { func (this *Lcd) BacklightOff() error { this.backlight = false err := this.writeByte(0x00, 0) - time.Sleep(10 * time.Millisecond) //Slight delay seems to help stablize on RPi3 + time.Sleep(250 * time.Millisecond) // Used delay as specified in https://github.com/duinoWitchery/hd44780/blob/master/hd44780.cpp (Line 887) if err != nil { return err } @@ -301,13 +343,81 @@ func (this *Lcd) BacklightOff() error { func (this *Lcd) Clear() error { err := this.writeByte(CMD_Clear_Display, 0) - time.Sleep(10 * time.Millisecond) //Slight delay seems to help stablize on RPi3 + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). return err } func (this *Lcd) Home() error { err := this.writeByte(CMD_Return_Home, 0) - time.Sleep(10 * time.Millisecond) //Slight delay seems to help stablize on RPi3 + time.Sleep(2 * time.Millisecond) // Page 24 of datasheet says 1.52ms to execute. We will do slightly longer delay. + return err +} + +func (this *Lcd) DisplayOn() error { + this.displayControl |= OPT_Enable_Display + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err +} + +func (this *Lcd) DisplayOff() error { + this.displayControl = this.displayControl &^ OPT_Enable_Display + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err +} + +func (this *Lcd) BlinkOn() error { + this.displayControl |= OPT_Enable_Blink + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err +} + +func (this *Lcd) BlinkOff() error { + this.displayControl = this.displayControl &^ OPT_Enable_Blink + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err +} + +func (this *Lcd) CursorOn() error { + this.displayControl |= OPT_Enable_Cursor + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err +} + +func (this *Lcd) CursorOff() error { + this.displayControl = this.displayControl &^ OPT_Enable_Cursor + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err +} + +func (this *Lcd) ScrollDisplayLeft() error { + err := this.writeByte(CMD_Cursor_Shift | OPT_Display_Move | OPT_Move_Left, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err +} + +func (this *Lcd) ScrollDisplayRight() error { + err := this.writeByte(CMD_Cursor_Shift | OPT_Display_Move | OPT_Move_Right, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err +} + +func (this *Lcd) LeftRightDisplay() error { + this.displayMode |= OPT_EntryLeft + err := this.writeByte(CMD_Entry_Mode | this.displayMode, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err +} + +func (this *Lcd) RightLeftDisplay() error { + this.displayMode = this.displayMode &^ OPT_EntryLeft + err := this.writeByte(CMD_Entry_Mode | this.displayMode, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). return err } @@ -323,6 +433,11 @@ func (this *Lcd) getSize() (width, height int) { } func (this *Lcd) SetPosition(line, pos int) error { + //Not active, so don't try do anything + if (!this.active) { + return nil + } + w, h := this.getSize() if w != -1 && (pos < 0 || pos > w-1) { return fmt.Errorf("Cursor position %d "+ @@ -353,15 +468,58 @@ func (this *Lcd) Command(cmd byte) error { return err } -// GetStrobeDelays returns the SET and RESET strobe delays. +// GetStrobeDelays returns the WRITE and RESET strobe delays in microseconds. func (this *Lcd) GetStrobeDelays() (writeDelay, resetDelay uint16) { return this.writeStrobeDelay, this.resetStrobeDelay } -// SetStrobeDelays sets the SET and RESET strobe delays. -// These will be multiplied by time.microsecond. -// For RPi 3, 240 and 30 seem stable values to use. +// SetStrobeDelays sets the WRITE and RESET strobe delays in microseconds. func (this *Lcd) SetStrobeDelays(writeDelay, resetDelay uint16) { this.writeStrobeDelay = writeDelay this.resetStrobeDelay = resetDelay } + +// Fill will show the specified character across the entire display +func (this *Lcd) Fill(char rune) (error) { + //Not active, so don't try do anything + if (!this.active) { + return nil + } + + var width, height = this.getSize() + + // Invalid srceen size, do nothing + if (width * height <= 1) { + return nil + } + + // Fill the display line by line + for lineCount := 0; lineCount < height; lineCount++ { + // Move cursor to position + err := this.SetPosition(lineCount, 0) + if err != nil { + return err + } + + // Fill the line + for colCount := 0; colCount < width; colCount++ { + err = this.writeByte(byte(char), PIN_RS) + if err != nil { + return err + } + } + } + + return nil +} + +// Shutdown will cleanup the LCD display +func (this *Lcd) Shutdown() { + this.active = false //Set active to FALSE. This will "block" characters being written to display (check functions which check this flag) + time.Sleep(250 * time.Millisecond) //Sleep to allow for any instructions/commands to complete before we continue + + // Shutdown display + this.BacklightOff() + this.Clear() + this.Home() +} \ No newline at end of file From ede4dc5efa77b425b7b0efa85eabc1a4ed15c580 Mon Sep 17 00:00:00 2001 From: adrianh-za <5121135+adrianh-za@users.noreply.github.com> Date: Sat, 17 Sep 2022 20:25:11 +0100 Subject: [PATCH 4/4] Add two new examples. Fixed broken old example. Added Sratup() function to lcd.go. --- examples/datetime-16x2.go | 53 +++ examples/datetime-20x4.go | 57 +++ examples/example1/example1.go | 116 ++--- examples/example2/example2.go | 108 ++--- examples/example3/example3.go | 89 ++-- examples/example4/example4.go | 58 +-- examples/helloworld/main.go | 52 +-- go.mod | 17 + go.sum | 16 + lcd.go | 837 +++++++++++++++++----------------- 10 files changed, 782 insertions(+), 621 deletions(-) create mode 100755 examples/datetime-16x2.go create mode 100755 examples/datetime-20x4.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/examples/datetime-16x2.go b/examples/datetime-16x2.go new file mode 100755 index 0000000..ef4506f --- /dev/null +++ b/examples/datetime-16x2.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "log" + "time" + + hd44780 "github.com/adrianh-za/go-hd44780-rpi" + "github.com/d2r2/go-i2c" + logger "github.com/d2r2/go-logger" +) + +func checkError(err error) { + if err != nil { + log.Fatal(err) + } +} + +func main() { + //Stop the I2C module from spamming the console + logger.ChangePackageLogLevel("i2c", logger.InfoLevel) + + //Init I2C + i2c, err := i2c.NewI2C(0x27, 1) + checkError(err) + defer i2c.Close() + + //Init the display + lcd, err := hd44780.NewLcd(i2c, hd44780.LCD_16x2) + lcd.Startup() + lcd.SetupExit(true) //Setup CTRL-C to quit gracefully + + //Display the time + for { + lcd.Home() + t := time.Now() + seconds := t.Second() + + lcd.SetPosition(0, 0) + fmt.Fprint(lcd, t.Format("Monday Jan 2")) + lcd.SetPosition(1, 1) + fmt.Fprint(lcd, t.Format("15:04:05 2006")) + + //Small sleeps until we need to update due to second change. + for { + if seconds != time.Now().Second() { + break + } + + time.Sleep(200 * time.Millisecond) + } + } +} diff --git a/examples/datetime-20x4.go b/examples/datetime-20x4.go new file mode 100755 index 0000000..7a497e2 --- /dev/null +++ b/examples/datetime-20x4.go @@ -0,0 +1,57 @@ +package main + +import ( + "fmt" + "log" + "time" + + hd44780 "github.com/adrianh-za/go-hd44780-rpi" + "github.com/d2r2/go-i2c" + logger "github.com/d2r2/go-logger" +) + +func checkError(err error) { + if err != nil { + log.Fatal(err) + } +} + +func main() { + //Stop the I2C module from spamming the console + logger.ChangePackageLogLevel("i2c", logger.InfoLevel) + + //Init I2C + i2c, err := i2c.NewI2C(0x27, 1) + checkError(err) + defer i2c.Close() + + //Init the display + lcd, err := hd44780.NewLcd(i2c, hd44780.LCD_20x4) + lcd.Startup() + lcd.SetupExit(true) //Setup CTRL-C to quit gracefully + + //Display the time + for { + lcd.Home() + t := time.Now() + seconds := t.Second() + + lcd.SetPosition(0, 0) + fmt.Fprint(lcd, t.Format("Monday Jan 2")) + lcd.SetPosition(1, 1) + fmt.Fprint(lcd, t.Format("15:04:05 2006")) + lcd.SetPosition(2, 0) + fmt.Fprint(lcd, t.Format("Monday Jan 2")) + lcd.SetPosition(3, 1) + fmt.Fprint(lcd, t.Format("15:04:05 2006")) + + //Small sleeps until we need to update due to second change. + for { + if seconds != time.Now().Second() { + break + } + + time.Sleep(200 * time.Millisecond) + } + } +} diff --git a/examples/example1/example1.go b/examples/example1/example1.go index ae6219c..860375e 100644 --- a/examples/example1/example1.go +++ b/examples/example1/example1.go @@ -1,70 +1,70 @@ package main import ( - //"bytes" - "fmt" - "log" - "time" + "bytes" + "fmt" + "log" + "time" - device "github.com/adrianh-za/go-hd44780" - "github.com/d2r2/go-i2c" + device "github.com/d2r2/go-hd44780" + "github.com/d2r2/go-i2c" ) func checkError(err error) { - if err != nil { - log.Fatal(err) - } + if err != nil { + log.Fatal(err) + } } func main() { - i2c, err := i2c.NewI2C(0x27, 1) - checkError(err) - defer i2c.Close() - lcd, err := device.NewLcd(i2c, device.LCD_16x2) - checkError(err) - err = lcd.BacklightOn() - checkError(err) - err = lcd.ShowMessage("--=! Let's rock !=--", device.SHOW_LINE_1) - checkError(err) - err = lcd.ShowMessage("Welcome to RPi dude!", device.SHOW_LINE_2) - checkError(err) - // err = lcd.ShowMessage("I'm lazy to be lazy.", device.SHOW_LINE_3) - // checkError(err) - // err = lcd.ShowMessage("R2D2, where are you?", device.SHOW_LINE_4) - // checkError(err) - /*time.Sleep(5 * time.Second) - for i := 0; i <= 12; i++ { - var buf bytes.Buffer - for j := 0; j <= 19; j++ { - buf.Write([]byte{byte(i*20 + j)}) - } - err = lcd.ShowMessage(buf.String(), device.SHOW_LINE_1) - checkError(err) - time.Sleep(1 * time.Second) - } - time.Sleep(5 * time.Second) - err = lcd.TestWriteCGRam() - checkError(err) - for i := 0; i <= 12; i++ { - var buf bytes.Buffer - for j := 0; j <= 19; j++ { - buf.Write([]byte{byte(i*20 + j)}) - } - err = lcd.ShowMessage(buf.String(), device.SHOW_LINE_1) - checkError(err) - time.Sleep(1 * time.Second) - }*/ - lcd.Clear() - for { - lcd.Home() - t := time.Now() - lcd.SetPosition(0, 0) - fmt.Fprint(lcd, t.Format("Monday Jan 2")) - lcd.SetPosition(1, 1) - fmt.Fprint(lcd, t.Format("15:04:05 2006")) - // lcd.SetPosition(4, 0) - // fmt.Fprint(lcd, "i2c, VGA, and Go") - time.Sleep(666 * time.Millisecond) - } + i2c, err := i2c.NewI2C(0x27, 1) + checkError(err) + defer i2c.Close() + lcd, err := device.NewLcd(i2c, device.LCD_16x2) + checkError(err) + err = lcd.BacklightOn() + checkError(err) + err = lcd.ShowMessage("--=! Let's rock !=--", device.SHOW_LINE_1) + checkError(err) + err = lcd.ShowMessage("Welcome to RPi dude!", device.SHOW_LINE_2) + checkError(err) + // err = lcd.ShowMessage("I'm lazy to be lazy.", device.SHOW_LINE_3) + // checkError(err) + // err = lcd.ShowMessage("R2D2, where are you?", device.SHOW_LINE_4) + // checkError(err) + time.Sleep(5 * time.Second) + for i := 0; i <= 12; i++ { + var buf bytes.Buffer + for j := 0; j <= 19; j++ { + buf.Write([]byte{byte(i*20 + j)}) + } + err = lcd.ShowMessage(buf.String(), device.SHOW_LINE_1) + checkError(err) + time.Sleep(1 * time.Second) + } + time.Sleep(5 * time.Second) + err = lcd.TestWriteCGRam() + checkError(err) + for i := 0; i <= 12; i++ { + var buf bytes.Buffer + for j := 0; j <= 19; j++ { + buf.Write([]byte{byte(i*20 + j)}) + } + err = lcd.ShowMessage(buf.String(), device.SHOW_LINE_1) + checkError(err) + time.Sleep(1 * time.Second) + } + lcd.Clear() + for { + lcd.Home() + t := time.Now() + lcd.SetPosition(1, 0) + fmt.Fprint(lcd, t.Format("Monday Jan 2")) + lcd.SetPosition(2, 1) + fmt.Fprint(lcd, t.Format("15:04:05 2006")) + // lcd.SetPosition(4, 0) + // fmt.Fprint(lcd, "i2c, VGA, and Go") + time.Sleep(666 * time.Millisecond) + } } diff --git a/examples/example2/example2.go b/examples/example2/example2.go index b4cdc7e..df0d72e 100755 --- a/examples/example2/example2.go +++ b/examples/example2/example2.go @@ -1,69 +1,69 @@ package main import ( - "fmt" - "log" - "strings" - "sync" - "time" + "fmt" + "log" + "strings" + "sync" + "time" - sensor "github.com/d2r2/go-dht" - device "github.com/d2r2/go-hd44780" - "github.com/d2r2/go-i2c" + sensor "github.com/d2r2/go-dht" + device "github.com/d2r2/go-hd44780" + "github.com/d2r2/go-i2c" ) func checkError(err error) { - if err != nil { - log.Fatal(err) - } + if err != nil { + log.Fatal(err) + } } func writeTime(lcd *device.Lcd, t time.Time, blink bool, m *sync.Mutex) error { - msg := t.Format("02/1/06 15:04 MST") - if blink { - msg = strings.Replace(msg, ":", " ", 1) - } - m.Lock() - defer m.Unlock() - err := lcd.ShowMessage(msg, device.SHOW_LINE_1|device.SHOW_ELIPSE_IF_NOT_FIT) - if err != nil { - return err - } - return nil + msg := t.Format("02/1/06 15:04 MST") + if blink { + msg = strings.Replace(msg, ":", " ", 1) + } + m.Lock() + defer m.Unlock() + err := lcd.ShowMessage(msg, device.SHOW_LINE_1|device.SHOW_ELIPSE_IF_NOT_FIT) + if err != nil { + return err + } + return nil } func main() { - i2c, err := i2c.NewI2C(0x27, 2) - checkError(err) - defer i2c.Close() - lcd, err := device.NewLcd(i2c, device.LCD_20x4) - checkError(err) - err = lcd.BacklightOn() - checkError(err) - m := &sync.Mutex{} - blink := false - go func() { - //writeTime(lcd, time.Now(), blink, m) - c := time.Tick(1 * time.Second) - for t := range c { - writeTime(lcd, t, blink, m) - blink = !blink - } - }() + i2c, err := i2c.NewI2C(0x27, 1) + checkError(err) + defer i2c.Close() + lcd, err := device.NewLcd(i2c, device.LCD_20x4) + checkError(err) + err = lcd.BacklightOn() + checkError(err) + m := &sync.Mutex{} + blink := false + go func() { + //writeTime(lcd, time.Now(), blink, m) + c := time.Tick(1 * time.Second) + for t := range c { + writeTime(lcd, t, blink, m) + blink = !blink + } + }() - for { - temp, hum, _, err := sensor.ReadDHTxxWithRetry(sensor.DHT22, 4, true, 5) - if err == nil { - m.Lock() - lcd.ShowMessage(fmt.Sprintf("T: %0.0f*C Hum: %0.0f%%", temp, hum), - device.SHOW_LINE_2|device.SHOW_ELIPSE_IF_NOT_FIT) - m.Unlock() - } else { - m.Lock() - lcd.ShowMessage("DHTxx read error", - device.SHOW_LINE_2|device.SHOW_ELIPSE_IF_NOT_FIT) - m.Unlock() - } - time.Sleep(10 * time.Second) - } + for { + temp, hum, _, err := sensor.ReadDHTxxWithRetry(sensor.DHT22, 4, true, 5) + if err == nil { + m.Lock() + lcd.ShowMessage(fmt.Sprintf("T: %0.0f*C Hum: %0.0f%%", temp, hum), + device.SHOW_LINE_2|device.SHOW_ELIPSE_IF_NOT_FIT) + m.Unlock() + } else { + m.Lock() + lcd.ShowMessage("DHTxx read error", + device.SHOW_LINE_2|device.SHOW_ELIPSE_IF_NOT_FIT) + m.Unlock() + } + time.Sleep(10 * time.Second) + } } diff --git a/examples/example3/example3.go b/examples/example3/example3.go index 14c09e4..dd0060f 100644 --- a/examples/example3/example3.go +++ b/examples/example3/example3.go @@ -1,57 +1,54 @@ package main import ( - "log" - "strings" - "time" + "log" + "strings" + "time" - device "github.com/adrianh-za/go-hd44780" - "github.com/d2r2/go-i2c" + device "github.com/d2r2/go-hd44780" + "github.com/d2r2/go-i2c" ) func checkError(err error) { - if err != nil { - log.Fatal(err) - } + if err != nil { + log.Fatal(err) + } } func main() { - i2c, err := i2c.NewI2C(0x27, 1) - checkError(err) - defer i2c.Close() - lcd, err := device.NewLcd(i2c, device.LCD_20x4) - //lcd.SetStrobeDelays(400, 50) - checkError(err) - err = lcd.BacklightOn() - checkError(err) - var msg = []string{ - "--=! Let's rock !=--", - "Welcome to RPi dude!", - "", - "R2D2, where are you?", - } - lines := []device.ShowOptions{device.SHOW_LINE_1, device.SHOW_LINE_2, - device.SHOW_LINE_3, device.SHOW_LINE_4} - i := 0 - for { - var j byte - for j = 0; j < 4; j++ { - k := (i + int(j)) % 4 - err = lcd.ShowMessage(msg[k], lines[j]) - checkError(err) - } - time.Sleep(2 * time.Second) - for j = 0; j < 4; j++ { - err = lcd.ShowMessage(strings.Repeat(" ", 20), lines[j]) - checkError(err) - } - time.Sleep(2 * time.Second) - //err = lcd.BacklightOff() - //checkError(err) - //time.Sleep(2 * time.Second) - //err = lcd.BacklightOn() - //checkError(err) - - i++ - } + i2c, err := i2c.NewI2C(0x27, 1) + checkError(err) + defer i2c.Close() + lcd, err := device.NewLcd(i2c, device.LCD_20x4) + checkError(err) + err = lcd.BacklightOn() + checkError(err) + var msg = []string{ + "--=! Let's rock !=--", + "Welcome to RPi dude!", + "", + "R2D2, where are you?", + } + lines := []device.ShowOptions{device.SHOW_LINE_1, device.SHOW_LINE_2, + device.SHOW_LINE_3, device.SHOW_LINE_4} + i := 0 + for { + var j byte + for j = 0; j < 4; j++ { + k := (i + int(j)) % 4 + err = lcd.ShowMessage(msg[k], lines[j]) + checkError(err) + } + time.Sleep(2 * time.Second) + for j = 0; j < 4; j++ { + err = lcd.ShowMessage(strings.Repeat(" ", 20), lines[j]) + checkError(err) + } + err = lcd.BacklightOff() + checkError(err) + time.Sleep(2 * time.Second) + err = lcd.BacklightOn() + checkError(err) + i++ + } } diff --git a/examples/example4/example4.go b/examples/example4/example4.go index e8672e8..dedb643 100644 --- a/examples/example4/example4.go +++ b/examples/example4/example4.go @@ -1,40 +1,40 @@ package main import ( - "log" - "time" + "log" + "time" - device "github.com/adrianh-za/go-hd44780" - "github.com/d2r2/go-i2c" + device "github.com/d2r2/go-hd44780" + "github.com/d2r2/go-i2c" ) func checkError(err error) { - if err != nil { - log.Fatal(err) - } + if err != nil { + log.Fatal(err) + } } func main() { - i2c, err := i2c.NewI2C(0x27, 1) - checkError(err) - defer i2c.Close() - lcd, err := device.NewLcd(i2c, device.LCD_20x4) - checkError(err) - err = lcd.BacklightOn() - checkError(err) - /* var msg = []string{ - "--=! Let's rock !=--", - "Welcome to RPi dude!", - "I'm lazy to be lazy.", - "R2D2, where are you?", - }*/ - err = lcd.ShowMessage("Hello world !!! How are you? How are you", - device.SHOW_LINE_1|device.SHOW_LINE_2| - device.SHOW_ELIPSE_IF_NOT_FIT|device.SHOW_BLANK_PADDING) - checkError(err) - time.Sleep(3 * time.Second) - err = lcd.ShowMessage("Welcome to RPi!!!", - device.SHOW_LINE_1|device.SHOW_LINE_2| - device.SHOW_ELIPSE_IF_NOT_FIT|device.SHOW_BLANK_PADDING) - checkError(err) + i2c, err := i2c.NewI2C(0x27, 1) + checkError(err) + defer i2c.Close() + lcd, err := device.NewLcd(i2c, device.LCD_20x4) + checkError(err) + err = lcd.BacklightOn() + checkError(err) + /* var msg = []string{ + "--=! Let's rock !=--", + "Welcome to RPi dude!", + "I'm lazy to be lazy.", + "R2D2, where are you?", + }*/ + err = lcd.ShowMessage("Hello world !!! How are you? How are you", + device.SHOW_LINE_1|device.SHOW_LINE_2| + device.SHOW_ELIPSE_IF_NOT_FIT|device.SHOW_BLANK_PADDING) + checkError(err) + time.Sleep(3 * time.Second) + err = lcd.ShowMessage("Welcome to RPi!!!", + device.SHOW_LINE_1|device.SHOW_LINE_2| + device.SHOW_ELIPSE_IF_NOT_FIT|device.SHOW_BLANK_PADDING) + checkError(err) } diff --git a/examples/helloworld/main.go b/examples/helloworld/main.go index 3d16cce..4742932 100755 --- a/examples/helloworld/main.go +++ b/examples/helloworld/main.go @@ -1,37 +1,37 @@ package main import ( - "fmt" - "log" - "time" + "fmt" + "log" + "time" - device "github.com/adrianh-za/go-hd44780" - i2c "github.com/d2r2/go-i2c" + device "github.com/d2r2/go-hd44780" + i2c "github.com/d2r2/go-i2c" ) func check(err error) { - if err != nil { - log.Fatal(err) - } + if err != nil { + log.Fatal(err) + } } func main() { - i2c, err := i2c.NewI2C(0x27, 1) - check(err) - defer i2c.Close() - lcd, err := device.NewLcd(i2c, device.LCD_16x2) - check(err) - lcd.BacklightOn() - lcd.Clear() - for { - lcd.Home() - t := time.Now() - lcd.SetPosition(0, 0) - fmt.Fprint(lcd, t.Format("Monday Jan 2")) - lcd.SetPosition(1, 0) - fmt.Fprint(lcd, t.Format("15:04:05 2006")) - // lcd.SetPosition(4, 0) - // fmt.Fprint(lcd, "i2c, VGA, and Go") - time.Sleep(333 * time.Millisecond) - } + i2c, err := i2c.NewI2C(0x27, 1) + check(err) + defer i2c.Close() + lcd, err := device.NewLcd(i2c, device.LCD_16x2) + check(err) + lcd.BacklightOn() + lcd.Clear() + for { + lcd.Home() + t := time.Now() + lcd.SetPosition(0, 0) + fmt.Fprint(lcd, t.Format("Monday Jan 2")) + lcd.SetPosition(1, 0) + fmt.Fprint(lcd, t.Format("15:04:05 2006")) + // lcd.SetPosition(4, 0) + // fmt.Fprint(lcd, "i2c, VGA, and Go") + time.Sleep(333 * time.Millisecond) + } } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..45c951a --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module github.com/adrianh-za/go-hd44780-rpi + +go 1.19 + +require ( + github.com/adrianh-za/go-hd44780 v0.0.0-20190721095655-a4e4ad3b2720 + github.com/d2r2/go-dht v0.0.0-20200119175940-4ba96621a218 + github.com/d2r2/go-hd44780 v0.0.0-20181002113701-74cc28c83a3e + github.com/d2r2/go-i2c v0.0.0-20191123181816-73a8a799d6bc + github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 +) + +require ( + github.com/d2r2/go-logger v0.0.0-20210606094344-60e9d1233e22 // indirect + github.com/d2r2/go-shell v0.0.0-20211022052110-f591c27e3e2e // indirect + github.com/davecgh/go-spew v1.1.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..82802b6 --- /dev/null +++ b/go.sum @@ -0,0 +1,16 @@ +github.com/adrianh-za/go-hd44780 v0.0.0-20190721095655-a4e4ad3b2720 h1:Iax6wdytaTDqfe6rpfI8iEGuBP2uZ/s9Tkh3puA1dm4= +github.com/adrianh-za/go-hd44780 v0.0.0-20190721095655-a4e4ad3b2720/go.mod h1:h/J3n8mOTAOd9VrLyazOSxX07+UD6AAfbqP9AOwCFlI= +github.com/d2r2/go-dht v0.0.0-20200119175940-4ba96621a218 h1:z3u7ZAkBvqHD2zaECn0qd1BwMCTiAYcQjFdU8vflVHo= +github.com/d2r2/go-dht v0.0.0-20200119175940-4ba96621a218/go.mod h1:AzSqP4S4/6pINOKg3VC79WC7YY3zskQcrXMFzphCry0= +github.com/d2r2/go-hd44780 v0.0.0-20181002113701-74cc28c83a3e h1:3gLJWdofXjBoecDb9e+giWp77saiF6r2Mtu+edWCksY= +github.com/d2r2/go-hd44780 v0.0.0-20181002113701-74cc28c83a3e/go.mod h1:IruYZr0O1UbQs3rV5N2WPM8CpaT5rRgvPPzksu1+N6o= +github.com/d2r2/go-i2c v0.0.0-20191123181816-73a8a799d6bc h1:HLRSIWzUGMLCq4ldt0W1GLs3nnAxa5EGoP+9qHgh6j0= +github.com/d2r2/go-i2c v0.0.0-20191123181816-73a8a799d6bc/go.mod h1:AwxDPnsgIpy47jbGXZHA9Rv7pDkOJvQbezPuK1Y+nNk= +github.com/d2r2/go-logger v0.0.0-20210606094344-60e9d1233e22 h1:nO+SY4KOMsF/LsZ5EtbSKhiT3M6sv/igo2PEru/xEHI= +github.com/d2r2/go-logger v0.0.0-20210606094344-60e9d1233e22/go.mod h1:eSx+YfcVy5vCjRZBNIhpIpfCGFMQ6XSOSQkDk7+VCpg= +github.com/d2r2/go-shell v0.0.0-20211022052110-f591c27e3e2e h1:6rbw4kecquuE5mELvn9DJqrFfTLkeITQSkv8chVAX2Q= +github.com/d2r2/go-shell v0.0.0-20211022052110-f591c27e3e2e/go.mod h1:yqtlOXB0bWzWgM4wZ9BdZ75OmXSiFYSKrZ3TZlPaePQ= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= diff --git a/lcd.go b/lcd.go index 8d7ad74..efb8486 100644 --- a/lcd.go +++ b/lcd.go @@ -1,525 +1,546 @@ package hd44780 import ( - "fmt" - "strings" - "time" - - "github.com/d2r2/go-i2c" + "fmt" + "strings" + "time" + "os" + "os/signal" + "syscall" + + "github.com/d2r2/go-i2c" ) const ( - // Commands - CMD_Clear_Display = 0x01 - CMD_Return_Home = 0x02 - CMD_Entry_Mode = 0x04 - CMD_Display_Control = 0x08 - CMD_Cursor_Shift = 0x10 - CMD_Function_Set = 0x20 - CMD_CGRAM_Set = 0x40 - CMD_DDRAM_Set = 0x80 - - // Flags for display entry mode (CMD_Entry_Mode) - OPT_EntryLeft = 0x02 - OPT_EntryRight = 0x00 - OPT_Increment = 0x01 - OPT_Decrement = 0x00 - - // Flags for display control (CMD_Display_Control) - OPT_Enable_Display = 0x04 - OPT_Disable_Display = 0x00 - OPT_Enable_Cursor = 0x02 - OPT_Disable_Cursor = 0x00 - OPT_Enable_Blink = 0x01 - OPT_Disable_Blink = 0x00 - - // Flags for display/cursor move () - OPT_Display_Move = 0x08 - OPT_Cursor_Move = 0x00 - OPT_Move_Right = 0x04 - OPT_Move_Left = 0x00 - - // Flags for function set (CMD_Function_Set) - OPT_8Bit_Mode = 0x10 - OPT_4Bit_Mode = 0x00 - OPT_2_Lines = 0x08 - OPT_1_Lines = 0x00 - OPT_5x10_Dots = 0x04 - OPT_5x8_Dots = 0x00 - - PIN_BACKLIGHT byte = 0x08 - PIN_EN byte = 0x04 // Enable bit - PIN_RW byte = 0x02 // Read/Write bit - PIN_RS byte = 0x01 // Register select bit + // Commands + CMD_Clear_Display = 0x01 + CMD_Return_Home = 0x02 + CMD_Entry_Mode = 0x04 + CMD_Display_Control = 0x08 + CMD_Cursor_Shift = 0x10 + CMD_Function_Set = 0x20 + CMD_CGRAM_Set = 0x40 + CMD_DDRAM_Set = 0x80 + + // Flags for display entry mode (CMD_Entry_Mode) + OPT_EntryLeft = 0x02 + OPT_EntryRight = 0x00 + OPT_Increment = 0x01 + OPT_Decrement = 0x00 + + // Flags for display control (CMD_Display_Control) + OPT_Enable_Display = 0x04 + OPT_Disable_Display = 0x00 + OPT_Enable_Cursor = 0x02 + OPT_Disable_Cursor = 0x00 + OPT_Enable_Blink = 0x01 + OPT_Disable_Blink = 0x00 + + // Flags for display/cursor move () + OPT_Display_Move = 0x08 + OPT_Cursor_Move = 0x00 + OPT_Move_Right = 0x04 + OPT_Move_Left = 0x00 + + // Flags for function set (CMD_Function_Set) + OPT_8Bit_Mode = 0x10 + OPT_4Bit_Mode = 0x00 + OPT_2_Lines = 0x08 + OPT_1_Lines = 0x00 + OPT_5x10_Dots = 0x04 + OPT_5x8_Dots = 0x00 + + PIN_BACKLIGHT byte = 0x08 + PIN_EN byte = 0x04 // Enable bit + PIN_RW byte = 0x02 // Read/Write bit + PIN_RS byte = 0x01 // Register select bit ) type LcdType int const ( - LCD_UNKNOWN LcdType = iota - LCD_16x2 - LCD_20x4 + LCD_UNKNOWN LcdType = iota + LCD_16x2 + LCD_20x4 ) type ShowOptions int const ( - SHOW_NO_OPTIONS ShowOptions = 0 - SHOW_LINE_1 = 1 << iota - SHOW_LINE_2 - SHOW_LINE_3 - SHOW_LINE_4 - SHOW_ELIPSE_IF_NOT_FIT - SHOW_BLANK_PADDING + SHOW_NO_OPTIONS ShowOptions = 0 + SHOW_LINE_1 = 1 << iota + SHOW_LINE_2 + SHOW_LINE_3 + SHOW_LINE_4 + SHOW_ELIPSE_IF_NOT_FIT + SHOW_BLANK_PADDING ) type Lcd struct { - i2c *i2c.I2C - backlight bool - lcdType LcdType - writeStrobeDelay uint16 - resetStrobeDelay uint16 - active bool - displayFunction byte - displayControl byte - displayMode byte + i2c *i2c.I2C + backlight bool + lcdType LcdType + writeStrobeDelay uint16 + resetStrobeDelay uint16 + active bool + displayFunction byte + displayControl byte + displayMode byte } func NewLcd(i2c *i2c.I2C, lcdType LcdType) (*Lcd, error) { - this := &Lcd{i2c: i2c, - backlight: false, - lcdType: lcdType, - writeStrobeDelay: 200, - resetStrobeDelay: 30, - active: true, - displayFunction: 0x00, - displayControl: 0x00, - displayMode: 0x00, - } - - // Wait is required during initialization steps. Various info below about delays. - // https://www.sparkfun.com/datasheets/LCD/HD44780.pdf (page 45) - // https://github.com/mrmorphic/hwio/blob/master/devices/hd44780/hd44780_i2c.go - // https://github.com/duinoWitchery/hd44780/blob/master/hd44780.cpp (read the comments) - - // Initial delay as per datasheet (need at least 40ms after power rises above 2.7V before sending commands.) - time.Sleep(100 * time.Millisecond) // Wait 100ms vs 40ms - - // Step 1 -> Base initialization sent with safe minimum delay afterwards - var err = this.writeByte(0x03, 0) - if err != nil { - return nil, err - } - time.Sleep(5 * time.Millisecond) // Wait 5ms vs 4.1ms - - // Step 2 -> Base initialization sent with safe minimum delay afterwards - err = this.writeByte(0x03, 0) - if err != nil { - return nil, err - } - time.Sleep(1 * time.Millisecond) // Wait 1ms vs 100us - - // Step 3 -> Base initialization sent with safe minimum delay afterwards - err = this.writeByte(0x03, 0) - if err != nil { - return nil, err - } - time.Sleep(1 * time.Millisecond) // Wait 1ms vs 100us - - // Step 4 -> 4-bit transfer mode sent with safe minimum delay afterwards - err = this.writeByte(0x02, 0) - if err != nil { - return nil, err - } - time.Sleep(1 * time.Millisecond) // Wait 1ms vs 100us - - // Step 5a -> Execute FUNCTIONSET command - this.displayFunction = OPT_2_Lines | OPT_5x8_Dots | OPT_4Bit_Mode - err = this.writeByte(CMD_Function_Set | this.displayFunction, 0) - time.Sleep(1 * time.Millisecond) // Wait 1ms to be safe - if err != nil { - return nil, err - } - - // Step 5b -> Execute DISPLAYCONTROL command - this.displayControl = OPT_Enable_Display | OPT_Disable_Cursor | OPT_Disable_Blink - err = this.writeByte(CMD_Display_Control | this.displayControl, 0) - time.Sleep(1 * time.Millisecond) // Wait 1ms to be safe - if err != nil { - return nil, err - } - - // Step 5c -> Execute ENTRYMODE command - this.displayMode = OPT_EntryLeft - err = this.writeByte(CMD_Entry_Mode | this.displayMode, 0) - time.Sleep(1 * time.Millisecond) // Wait 1ms to be safe - if err != nil { - return nil, err - - } - - // Clear the display - err = this.Clear() - if err != nil { - return nil, err - } - - // Send cursor to home - err = this.Home() - if err != nil { - return nil, err - } - - return this, nil + this := &Lcd{i2c: i2c, + backlight: false, + lcdType: lcdType, + writeStrobeDelay: 200, + resetStrobeDelay: 30, + active: true, + displayFunction: 0x00, + displayControl: 0x00, + displayMode: 0x00, + } + + // Wait is required during initialization steps. Various info below about delays. + // https://www.sparkfun.com/datasheets/LCD/HD44780.pdf (page 45) + // https://github.com/mrmorphic/hwio/blob/master/devices/hd44780/hd44780_i2c.go + // https://github.com/duinoWitchery/hd44780/blob/master/hd44780.cpp (read the comments) + + // Initial delay as per datasheet (need at least 40ms after power rises above 2.7V before sending commands.) + time.Sleep(100 * time.Millisecond) // Wait 100ms vs 40ms + + // Step 1 -> Base initialization sent with safe minimum delay afterwards + var err = this.writeByte(0x03, 0) + if err != nil { + return nil, err + } + time.Sleep(5 * time.Millisecond) // Wait 5ms vs 4.1ms + + // Step 2 -> Base initialization sent with safe minimum delay afterwards + err = this.writeByte(0x03, 0) + if err != nil { + return nil, err + } + time.Sleep(1 * time.Millisecond) // Wait 1ms vs 100us + + // Step 3 -> Base initialization sent with safe minimum delay afterwards + err = this.writeByte(0x03, 0) + if err != nil { + return nil, err + } + time.Sleep(1 * time.Millisecond) // Wait 1ms vs 100us + + // Step 4 -> 4-bit transfer mode sent with safe minimum delay afterwards + err = this.writeByte(0x02, 0) + if err != nil { + return nil, err + } + time.Sleep(1 * time.Millisecond) // Wait 1ms vs 100us + + // Step 5a -> Execute FUNCTIONSET command + this.displayFunction = OPT_2_Lines | OPT_5x8_Dots | OPT_4Bit_Mode + err = this.writeByte(CMD_Function_Set | this.displayFunction, 0) + time.Sleep(1 * time.Millisecond) // Wait 1ms to be safe + if err != nil { + return nil, err + } + + // Step 5b -> Execute DISPLAYCONTROL command + this.displayControl = OPT_Enable_Display | OPT_Disable_Cursor | OPT_Disable_Blink + err = this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(1 * time.Millisecond) // Wait 1ms to be safe + if err != nil { + return nil, err + } + + // Step 5c -> Execute ENTRYMODE command + this.displayMode = OPT_EntryLeft + err = this.writeByte(CMD_Entry_Mode | this.displayMode, 0) + time.Sleep(1 * time.Millisecond) // Wait 1ms to be safe + if err != nil { + return nil, err + } + + // Clear the display + err = this.Clear() + if err != nil { + return nil, err + } + + // Send cursor to home + err = this.Home() + if err != nil { + return nil, err + } + + return this, nil } type rawData struct { - Data byte - Delay time.Duration + Data byte + Delay time.Duration } func (this *Lcd) writeRawDataSeq(seq []rawData) error { - for _, item := range seq { - _, err := this.i2c.WriteBytes([]byte{item.Data}) - if err != nil { - return err - } - time.Sleep(item.Delay) - } - return nil + for _, item := range seq { + _, err := this.i2c.WriteBytes([]byte{item.Data}) + if err != nil { + return err + } + time.Sleep(item.Delay) + } + return nil } func (this *Lcd) writeDataWithStrobe(data byte) error { - if this.backlight { - data |= PIN_BACKLIGHT - } - seq := []rawData{ - {data, 0}, // send data - {data | PIN_EN, time.Duration(this.writeStrobeDelay) * time.Microsecond}, // set strobe - {data, time.Duration(this.resetStrobeDelay) * time.Microsecond}, // reset strobe - } - return this.writeRawDataSeq(seq) + if this.backlight { + data |= PIN_BACKLIGHT + } + seq := []rawData{ + {data, 0}, // send data + {data | PIN_EN, time.Duration(this.writeStrobeDelay) * time.Microsecond}, // set strobe + {data, time.Duration(this.resetStrobeDelay) * time.Microsecond}, // reset strobe + } + return this.writeRawDataSeq(seq) } func (this *Lcd) writeByte(data byte, controlPins byte) error { - err := this.writeDataWithStrobe(data&0xF0 | controlPins) - if err != nil { - return err - } - err = this.writeDataWithStrobe((data<<4)&0xF0 | controlPins) - if err != nil { - return err - } - return nil + err := this.writeDataWithStrobe(data&0xF0 | controlPins) + if err != nil { + return err + } + err = this.writeDataWithStrobe((data<<4)&0xF0 | controlPins) + if err != nil { + return err + } + return nil } func (this *Lcd) getLineRange(options ShowOptions) (startLine, endLine int) { - var lines [4]bool - lines[0] = options&SHOW_LINE_1 != 0 - lines[1] = options&SHOW_LINE_2 != 0 - lines[2] = options&SHOW_LINE_3 != 0 - lines[3] = options&SHOW_LINE_4 != 0 - startLine = -1 - for i := 0; i < len(lines); i++ { - if lines[i] { - startLine = i - break - } - } - endLine = -1 - for i := len(lines) - 1; i >= 0; i-- { - if lines[i] { - endLine = i - break - } - } - return startLine, endLine + var lines [4]bool + lines[0] = options&SHOW_LINE_1 != 0 + lines[1] = options&SHOW_LINE_2 != 0 + lines[2] = options&SHOW_LINE_3 != 0 + lines[3] = options&SHOW_LINE_4 != 0 + startLine = -1 + for i := 0; i < len(lines); i++ { + if lines[i] { + startLine = i + break + } + } + endLine = -1 + for i := len(lines) - 1; i >= 0; i-- { + if lines[i] { + endLine = i + break + } + } + return startLine, endLine } func (this *Lcd) splitText(text string, options ShowOptions) []string { - var lines []string - startLine, endLine := this.getLineRange(options) - w, _ := this.getSize() - if w != -1 && startLine != -1 && endLine != -1 { - for i := 0; i <= endLine-startLine; i++ { - if len(text) == 0 { - break - } - j := w - if j > len(text) { - j = len(text) - } - lines = append(lines, text[:j]) - text = text[j:] - } - if len(text) > 0 { - if options&SHOW_ELIPSE_IF_NOT_FIT != 0 { - j := len(lines) - 1 - lines[j] = lines[j][:len(lines[j])-1] + "~" - } - } else { - if options&SHOW_BLANK_PADDING != 0 { - j := len(lines) - 1 - lines[j] = lines[j] + strings.Repeat(" ", w-len(lines[j])) - for k := j + 1; k <= endLine-startLine; k++ { - lines = append(lines, strings.Repeat(" ", w)) - } - } - - } - } else if len(text) > 0 { - lines = append(lines, text) - } - return lines + var lines []string + startLine, endLine := this.getLineRange(options) + w, _ := this.getSize() + if w != -1 && startLine != -1 && endLine != -1 { + for i := 0; i <= endLine-startLine; i++ { + if len(text) == 0 { + break + } + j := w + if j > len(text) { + j = len(text) + } + lines = append(lines, text[:j]) + text = text[j:] + } + if len(text) > 0 { + if options&SHOW_ELIPSE_IF_NOT_FIT != 0 { + j := len(lines) - 1 + lines[j] = lines[j][:len(lines[j])-1] + "~" + } + } else { + if options&SHOW_BLANK_PADDING != 0 { + j := len(lines) - 1 + lines[j] = lines[j] + strings.Repeat(" ", w-len(lines[j])) + for k := j + 1; k <= endLine-startLine; k++ { + lines = append(lines, strings.Repeat(" ", w)) + } + } + + } + } else if len(text) > 0 { + lines = append(lines, text) + } + return lines } func (this *Lcd) ShowMessage(text string, options ShowOptions) error { - //Not active, so don't try do anything - if (!this.active) { - return nil - } - - lines := this.splitText(text, options) - log.Debug("Output: %v\n", lines) - startLine, endLine := this.getLineRange(options) - i := 0 - for { - if startLine != -1 && endLine != -1 { - err := this.SetPosition(i+startLine, 0) - if err != nil { - return err - } - } - line := lines[i] - for _, c := range line { - err := this.writeByte(byte(c), PIN_RS) - if err != nil { - return err - } - } - if i == len(lines)-1 { - break - } - i++ - } - return nil + //Not active, so don't try do anything + if (!this.active) { + return nil + } + + lines := this.splitText(text, options) + log.Debug("Output: %v\n", lines) + startLine, endLine := this.getLineRange(options) + i := 0 + for { + if startLine != -1 && endLine != -1 { + err := this.SetPosition(i+startLine, 0) + if err != nil { + return err + } + } + line := lines[i] + for _, c := range line { + err := this.writeByte(byte(c), PIN_RS) + if err != nil { + return err + } + } + if i == len(lines)-1 { + break + } + i++ + } + return nil } func (this *Lcd) TestWriteCGRam() error { - err := this.writeByte(CMD_CGRAM_Set, 0) - if err != nil { - return err - } - var a byte = 0x55 - for i := 0; i < 80; i++ { - err := this.writeByte(a, PIN_RS) - if err != nil { - return err - } - a = a ^ 0xFF - } - return nil + err := this.writeByte(CMD_CGRAM_Set, 0) + if err != nil { + return err + } + var a byte = 0x55 + for i := 0; i < 80; i++ { + err := this.writeByte(a, PIN_RS) + if err != nil { + return err + } + a = a ^ 0xFF + } + return nil } func (this *Lcd) BacklightOn() error { - this.backlight = true - err := this.writeByte(0x00, 0) - time.Sleep(100 * time.Millisecond) // Used delay as specified in https://github.com/duinoWitchery/hd44780/blob/master/hd44780.cpp (Line 885) - if err != nil { - return err - } - return nil + this.backlight = true + err := this.writeByte(0x00, 0) + time.Sleep(100 * time.Millisecond) // Used delay as specified in https://github.com/duinoWitchery/hd44780/blob/master/hd44780.cpp (Line 885) + if err != nil { + return err + } + return nil } func (this *Lcd) BacklightOff() error { - this.backlight = false - err := this.writeByte(0x00, 0) - time.Sleep(250 * time.Millisecond) // Used delay as specified in https://github.com/duinoWitchery/hd44780/blob/master/hd44780.cpp (Line 887) - if err != nil { - return err - } - return nil + this.backlight = false + err := this.writeByte(0x00, 0) + time.Sleep(250 * time.Millisecond) // Used delay as specified in https://github.com/duinoWitchery/hd44780/blob/master/hd44780.cpp (Line 887) + if err != nil { + return err + } + return nil } func (this *Lcd) Clear() error { - err := this.writeByte(CMD_Clear_Display, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + err := this.writeByte(CMD_Clear_Display, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) Home() error { - err := this.writeByte(CMD_Return_Home, 0) - time.Sleep(2 * time.Millisecond) // Page 24 of datasheet says 1.52ms to execute. We will do slightly longer delay. - return err + err := this.writeByte(CMD_Return_Home, 0) + time.Sleep(2 * time.Millisecond) // Page 24 of datasheet says 1.52ms to execute. We will do slightly longer delay. + return err } func (this *Lcd) DisplayOn() error { - this.displayControl |= OPT_Enable_Display - err := this.writeByte(CMD_Display_Control | this.displayControl, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + this.displayControl |= OPT_Enable_Display + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) DisplayOff() error { - this.displayControl = this.displayControl &^ OPT_Enable_Display - err := this.writeByte(CMD_Display_Control | this.displayControl, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + this.displayControl = this.displayControl &^ OPT_Enable_Display + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) BlinkOn() error { - this.displayControl |= OPT_Enable_Blink - err := this.writeByte(CMD_Display_Control | this.displayControl, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + this.displayControl |= OPT_Enable_Blink + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) BlinkOff() error { - this.displayControl = this.displayControl &^ OPT_Enable_Blink - err := this.writeByte(CMD_Display_Control | this.displayControl, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + this.displayControl = this.displayControl &^ OPT_Enable_Blink + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) CursorOn() error { - this.displayControl |= OPT_Enable_Cursor - err := this.writeByte(CMD_Display_Control | this.displayControl, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + this.displayControl |= OPT_Enable_Cursor + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) CursorOff() error { - this.displayControl = this.displayControl &^ OPT_Enable_Cursor - err := this.writeByte(CMD_Display_Control | this.displayControl, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + this.displayControl = this.displayControl &^ OPT_Enable_Cursor + err := this.writeByte(CMD_Display_Control | this.displayControl, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) ScrollDisplayLeft() error { - err := this.writeByte(CMD_Cursor_Shift | OPT_Display_Move | OPT_Move_Left, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + err := this.writeByte(CMD_Cursor_Shift | OPT_Display_Move | OPT_Move_Left, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) ScrollDisplayRight() error { - err := this.writeByte(CMD_Cursor_Shift | OPT_Display_Move | OPT_Move_Right, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + err := this.writeByte(CMD_Cursor_Shift | OPT_Display_Move | OPT_Move_Right, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) LeftRightDisplay() error { - this.displayMode |= OPT_EntryLeft - err := this.writeByte(CMD_Entry_Mode | this.displayMode, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + this.displayMode |= OPT_EntryLeft + err := this.writeByte(CMD_Entry_Mode | this.displayMode, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) RightLeftDisplay() error { - this.displayMode = this.displayMode &^ OPT_EntryLeft - err := this.writeByte(CMD_Entry_Mode | this.displayMode, 0) - time.Sleep(2 * time.Millisecond) // Do same delay as Home(). - return err + this.displayMode = this.displayMode &^ OPT_EntryLeft + err := this.writeByte(CMD_Entry_Mode | this.displayMode, 0) + time.Sleep(2 * time.Millisecond) // Do same delay as Home(). + return err } func (this *Lcd) getSize() (width, height int) { - switch this.lcdType { - case LCD_16x2: - return 16, 2 - case LCD_20x4: - return 20, 4 - default: - return -1, -1 - } + switch this.lcdType { + case LCD_16x2: + return 16, 2 + case LCD_20x4: + return 20, 4 + default: + return -1, -1 + } } func (this *Lcd) SetPosition(line, pos int) error { - //Not active, so don't try do anything - if (!this.active) { - return nil - } - - w, h := this.getSize() - if w != -1 && (pos < 0 || pos > w-1) { - return fmt.Errorf("Cursor position %d "+ - "must be within the range [0..%d]", pos, w-1) - } - if h != -1 && (line < 0 || line > h-1) { - return fmt.Errorf("Cursor line %d "+ - "must be within the range [0..%d]", line, h-1) - } - lineOffset := []byte{0x00, 0x40, 0x14, 0x54} - var b byte = CMD_DDRAM_Set + lineOffset[line] + byte(pos) - err := this.writeByte(b, 0) - return err + //Not active, so don't try do anything + if (!this.active) { + return nil + } + + w, h := this.getSize() + if w != -1 && (pos < 0 || pos > w-1) { + return fmt.Errorf("Cursor position %d "+ + "must be within the range [0..%d]", pos, w-1) + } + if h != -1 && (line < 0 || line > h-1) { + return fmt.Errorf("Cursor line %d "+ + "must be within the range [0..%d]", line, h-1) + } + lineOffset := []byte{0x00, 0x40, 0x14, 0x54} + var b byte = CMD_DDRAM_Set + lineOffset[line] + byte(pos) + err := this.writeByte(b, 0) + return err } func (this *Lcd) Write(buf []byte) (int, error) { - for i, c := range buf { - err := this.writeByte(c, PIN_RS) - if err != nil { - return i, err - } - } - return len(buf), nil + for i, c := range buf { + err := this.writeByte(c, PIN_RS) + if err != nil { + return i, err + } + } + return len(buf), nil } func (this *Lcd) Command(cmd byte) error { - err := this.writeByte(cmd, 0) - return err + err := this.writeByte(cmd, 0) + return err } // GetStrobeDelays returns the WRITE and RESET strobe delays in microseconds. func (this *Lcd) GetStrobeDelays() (writeDelay, resetDelay uint16) { - return this.writeStrobeDelay, this.resetStrobeDelay + return this.writeStrobeDelay, this.resetStrobeDelay } // SetStrobeDelays sets the WRITE and RESET strobe delays in microseconds. func (this *Lcd) SetStrobeDelays(writeDelay, resetDelay uint16) { - this.writeStrobeDelay = writeDelay - this.resetStrobeDelay = resetDelay + this.writeStrobeDelay = writeDelay + this.resetStrobeDelay = resetDelay } // Fill will show the specified character across the entire display func (this *Lcd) Fill(char rune) (error) { - //Not active, so don't try do anything - if (!this.active) { - return nil - } - - var width, height = this.getSize() - - // Invalid srceen size, do nothing - if (width * height <= 1) { - return nil - } - - // Fill the display line by line - for lineCount := 0; lineCount < height; lineCount++ { - // Move cursor to position - err := this.SetPosition(lineCount, 0) - if err != nil { - return err - } - - // Fill the line - for colCount := 0; colCount < width; colCount++ { - err = this.writeByte(byte(char), PIN_RS) - if err != nil { - return err - } - } - } - - return nil + //Not active, so don't try do anything + if (!this.active) { + return nil + } + + var width, height = this.getSize() + + // Invalid srceen size, do nothing + if (width * height <= 1) { + return nil + } + + // Fill the display line by line + for lineCount := 0; lineCount < height; lineCount++ { + // Move cursor to position + err := this.SetPosition(lineCount, 0) + if err != nil { + return err + } + + // Fill the line + for colCount := 0; colCount < width; colCount++ { + err = this.writeByte(byte(char), PIN_RS) + if err != nil { + return err + } + } + } + + return nil +} + +// Startup +func (this *Lcd) Startup() { + this.BacklightOn() + this.Clear() + this.Home() } // Shutdown will cleanup the LCD display func (this *Lcd) Shutdown() { - this.active = false //Set active to FALSE. This will "block" characters being written to display (check functions which check this flag) - time.Sleep(250 * time.Millisecond) //Sleep to allow for any instructions/commands to complete before we continue - - // Shutdown display - this.BacklightOff() - this.Clear() - this.Home() + this.active = false //Set active to FALSE. This will "block" characters being written to display (check functions which check this flag) + time.Sleep(250 * time.Millisecond) //Sleep to allow for any instructions/commands to complete before we continue + + // Shutdown display + this.BacklightOff() + this.Clear() + this.Home() +} + +func (this *Lcd) SetupExit(clear bool) { + signalChan := make(chan os.Signal, 1) + signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM) + + go func() { + for range signalChan { + this.Shutdown(); + os.Exit(1) + } + }() } \ No newline at end of file