Tech Talk with Gus 10 - Seeed Studio Grove Modules

Today we talk about Seeed Studio Grove Modules and using them with our FEZ products.

3 Likes

Here is the code:

// original code https://github.com/Seeed-Studio/Grove_LCD_RGB_Backlight

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHI.IO;

namespace grove_test
{
    public static class rgb_lcd
    {
        static SoftwareI2CBus I2C = new SoftwareI2CBus(GHI.Pins.FEZCobraIII.Gpio.D19, GHI.Pins.FEZCobraIII.Gpio.D18);
        static byte LCD_ADDRESS=(0x7c>>1);

        static void i2c_send_byte(byte dta)
        {
            int written, read;
            byte[] wb = new byte[2];
            byte[] rb = new byte[2];
            wb[0] = dta;
            
            I2C.WriteRead(LCD_ADDRESS, wb, 0, 1, rb, 0, 0, out written, out read);
           
        }

        static void i2c_send_byteS(byte[] dta, byte len)
        {
            int written, read;
            //byte[] wb = new byte[2];
            byte[] rb = new byte[2];
            //wb[0] = dta;

            I2C.WriteRead(LCD_ADDRESS, dta, 0, len, rb, 0, 0, out written, out read);

           
        }
        static byte _displayfunction;
        static byte _displaycontrol;
        static byte _displaymode;
        static byte _numlines, _currline;


        static byte LCD_CLEARDISPLAY = 0x01;
        static byte LCD_RETURNHOME = 0x02;
        static byte LCD_ENTRYMODESET = 0x04;
        static byte LCD_DISPLAYCONTROL = 0x08;
        static byte LCD_CURSORSHIFT = 0x10;
        static byte LCD_FUNCTIONSET = 0x20;
        static byte LCD_SETCGRAMADDR = 0x40;
        static byte LCD_SETDDRAMADDR = 0x80;

        static byte LCD_DISPLAYON = 0x04;
        static byte LCD_DISPLAYOFF = 0x00;
        static byte LCD_CURSORON = 0x02;
        static byte LCD_CURSOROFF = 0x00;
        static byte LCD_BLINKON = 0x01;
        static byte LCD_BLINKOFF = 0x00;
        static byte LCD_ENTRYRIGHT = 0x00;
        static byte LCD_ENTRYLEFT = 0x02;
        static byte LCD_ENTRYSHIFTINCREMENT = 0x01;
        static byte LCD_ENTRYSHIFTDECREMENT = 0x00;

        static byte REG_MODE1 = 0x00;
        static byte REG_MODE2 = 0x01;
        static byte REG_OUTPUT = 0x08;


        public static void begin(byte cols, byte lines, byte dotsize)
        {

           

            if (lines > 1)
            {
                _displayfunction |= 0x08;// LCD_2LINE;
            }
            _numlines = lines;
            _currline = 0;

            // for some 1 line displays you can select a 10 pixel high font
            if ((dotsize != 0) && (lines == 1))
            {
                _displayfunction |= 0x04;// LCD_5x10DOTS;
            }

            // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
            // according to datasheet, we need at least 40ms after power rises above 2.7V
            // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
            //delayMicroseconds(50000);
            Thread.Sleep(50);

            // this is according to the hitachi HD44780 datasheet
            // page 45 figure 23

            // Send function set command sequence
            command((byte)(LCD_FUNCTIONSET | _displayfunction));
            //delayMicroseconds(4500);  // wait more than 4.1ms
            Thread.Sleep(5);

            // second try
            command((byte)(LCD_FUNCTIONSET | _displayfunction));
            //delayMicroseconds(150);
            Thread.Sleep(1);

            // third go
            command((byte)(LCD_FUNCTIONSET | _displayfunction));


            // finally, set # lines, font size, etc.
            command((byte)(LCD_FUNCTIONSET | _displayfunction));

            // turn the display on with no cursor or blinking default
            _displaycontrol = (byte)(LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
            display();

            // clear it off
            clear();

            // Initialize to default text direction (for romance languages)
            _displaymode = (byte)(LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT);
            // set the entry mode
            command((byte)(LCD_ENTRYMODESET | _displaymode));


            // backlight init
            setReg(REG_MODE1, 0);
            // set LEDs controllable by both PWM and GRPPWM registers
            setReg(REG_OUTPUT, 0xFF);
            // set MODE2 values
            // 0010 0000 -> 0x20  (DMBLNK to 1, ie blinky mode)
            setReg(REG_MODE2, 0x20);

            //setColorWhite();
            setRGB(255, 0, 100);
        }

        /********** high level commands, for the user! */
        static void clear()
        {
            command(LCD_CLEARDISPLAY);        // clear display, set cursor position to zero
            //delayMicroseconds(2000);          // this command takes a long time!
            Thread.Sleep(2);
        }

        static void home()
        {
            command(LCD_RETURNHOME);        // set cursor position to zero
            //delayMicroseconds(2000);        // this command takes a long time!
            Thread.Sleep(2);
        }

        public static void setCursor(byte col, byte row)
        {

            col = (byte)(row == 0 ? (col | 0x80) : (col | 0xc0));
            byte[] dta=new byte[2]  { 0x80, col };

            i2c_send_byteS(dta, 2);

        }

        // Turn the display on/off (quickly)
        static void noDisplay()
        {
            _displaycontrol &= (byte)~LCD_DISPLAYON;
            command((byte)(LCD_DISPLAYCONTROL | _displaycontrol));
        }

        static void display()
        {
            _displaycontrol |= LCD_DISPLAYON;
            command((byte)(LCD_DISPLAYCONTROL | _displaycontrol));
        }

       
        // Control the backlight LED blinking
        static void blinkLED()
        {
            // blink period in seconds = (<reg 7> + 1) / 24
            // on/off ratio = <reg 6> / 256
            setReg(0x07, 0x17);  // blink every second
            setReg(0x06, 0x7f);  // half on, half off
        }

        static void noBlinkLED()
        {
            setReg(0x07, 0x00);
            setReg(0x06, 0xff);
        }

        /*********** mid level commands, for sending data/cmds */

        // send command
        static void command(byte value)
        {
            byte[] dta = new byte[2] { 0x80, value };
            i2c_send_byteS(dta, 2);
        }

        // send data
        public static void write(byte value)
        {

            byte[] dta = new byte[2] { 0x40, value };
            i2c_send_byteS(dta, 2);
            //return 1; // assume sucess
        }
        public static void write(string s)
        {

            for (int i = 0; i < s.Length; i++)
                write((byte)s[i]);
        }

        static void setReg(byte addr, byte dta)
        {
            int written, read;
            byte[] wb = new byte[2];
            byte[] rb = new byte[2];
            wb[0] = addr;
            wb[1] = dta;


            I2C.WriteRead((0xc4 >> 1), wb, 0, 2, rb, 0, 0, out written, out read);
           
        }

        public static void setRGB(byte r, byte g, byte b)
        {

            byte REG_RED = 0x04;    // pwm2 
            byte REG_GREEN = 0x03;      // pwm1 
            byte REG_BLUE = 0x02;      // pwm0 

            setReg(REG_RED, r);
            setReg(REG_GREEN, g);
            setReg(REG_BLUE, b);
        }
       
    }
    
    public class Program
    {
        static void Bounce()
        {
            Bitmap LCD = new Bitmap(480, 272);
            int x = 50, y = 50;
            int dx = 1, dy = 1;
            int sx = 5, sy = 5;
            PulseFeedback PF = new PulseFeedback(PulseFeedback.Mode.EchoDuration, true, 10, GHI.Pins.FEZCobraIII.Gpio.D2);
            PF.ResistorMode = Port.ResistorMode.PullDown;

            while (true)
            {
                LCD.Clear();
                LCD.DrawEllipse(Microsoft.SPOT.Presentation.Media.Color.White, x, y, 10, 10);
                LCD.Flush();
                x += (dx * sx);
                y += (dy * sy);
                if (x < 0 || x > 480)
                    dx *= -1;
                if (y < 0 || y > 272)
                    dy *= -1;
                int speed= (int) (PF.Read() / 100);
                if (speed > 50)
                    speed = 50;
                sx = sy = speed;

                //Debug.Print("feed" + PF.Read());
                Thread.Sleep(50);
            }
        }
        public static void Main()
        {
            // make a ball bounce in a seperat thread
            Thread BounceThread = new Thread(Bounce);
            BounceThread.Start();

            // the main program thread, the grove demo
            AnalogInput A0 = new AnalogInput(GHI.Pins.FEZCobraIII.AnalogInput.D14);
            OutputPort D3 = new OutputPort(GHI.Pins.FEZCobraIII.Gpio.D3, false);
            OutputPort D4 = new OutputPort(GHI.Pins.FEZCobraIII.Gpio.D4, true);
            InputPort D8 = new InputPort(GHI.Pins.FEZCobraIII.Gpio.D8, false, Port.ResistorMode.PullUp);
            Random r = new Random();

            // test the RGB LCD. all I2C
            rgb_lcd.begin(6, 2, 0);
            Thread.Sleep(300);
            rgb_lcd.write((byte)'G');
            rgb_lcd.write((byte)'H');
            rgb_lcd.write((byte)'I');


            while(true)
            {
                int a = A0.ReadRaw()/4;
                double temperature;
                int B = 3975;
                float resistance = (float)(1023 - a) * 10000 / a;
                temperature = 1 / (System.Math.Log(resistance / 10000) / B + 1 / 298.15) - 273.15;
                temperature = temperature / 2;

                rgb_lcd.setCursor(0, 0);
                rgb_lcd.write("Temp: "+ temperature.ToString());
                if (temperature > 30)
                    D4.Write(true);
                else
                    D4.Write(false);

                if (temperature > 33)
                    D3.Write(true);
                else
                    D3.Write(false);
                if(D8.Read())
                {
                    
                    rgb_lcd.setRGB((byte)r.Next(), (byte)r.Next(), (byte)r.Next());
                }
                Thread.Sleep(300);
            }

        }
    }
}

Yeah I am special :slight_smile:

We redid the studio for better lighting. Hopefully future videos will look better.

Your limit checks are bounded incorrectly, since the “valid” range of display values is 0 to 479 (or 0 to 271 height). Besides the fact that you should use the dynamic screen size from the framework instead of hardcoding a value there and in your bitmap dimension.

if (x < 0 || x > 479)
                    dx *= -1;
                if (y < 0 || y > 271)
                    dy *= -1;
2 Likes

Hi Gus

I use GHI and Grove gear for teaching and personal projects, including my electric longboard. (It does >30Kph with a range of 10-15K depending on speed & terrain.)

For my prototype I modified a 5CM Grove cable so I could use Grove I2C modules with the dedicated I2C pins on my Fez Lemur.

The Seeedstudio base shields (plus other plug n play systems from vendors like electow.com, elecfreaks.com) are designed to work with *duino devices with the I2C on A4/A5 or the two dedicated pins (like the Arduino Uno R3 devices) it would be great if some future *duino formfactor GHI devices used these pinouts.

@ kiwiBryn
blog.devmobile.co.nz

3 Likes