Sunspot Home

more notes for a bad memory - - -

Using an Arduino Mega with colour LCD (UTFT) display as a dumb i2c Raspberry Pi slave

Objective
Use an Arduino Mega to drive a dumb LCD display as the i2c slave of a Raspberry Pi i2c master.
Create and manipulate the strings in the Pi and send them to any x/y coordinates on the 320 x 480 LCD

This display is from Banggood

3.2 Inch 320 X 480 TFT LCD Display Module (£5.99)
You can also get Mega clones for £5.35 - so a full colour 320x480 i2c slave display for a Pi for less than £12
- and you can have many of them on the same i2c bus
I use i2c long line drivers and run the bus at 10KHz - a very large network works well for the slow data of temperatures pump control etc.

pimegalcd

A C code program on the Pi sends out strings of bytes of chosen colour to an x/y coordinate on the LCD
The Arduino Mega receives the bytes and drives the display.

Command 2 is used for the strings
Command 3 clears the screen - - More to follow (change font etc)

String handling in C is impossible to remember if you only program every few months.
So I use Blassic basic to feed strings to the C code program.
See http://blassic.net/ . . . download the Raspberry Pi version of Blassic for /usr/sbin here)

Within a Blassic program you can run bits of C code and Bash scripts, read and write to files.
- and move from strings to ints to floats to bytes to ASCII characters etc. with trivial ease.


Python could do it - but I know basic and you can follow the program meaning in plain english!!

The code comments explain the detail.

This code is my current backup and will be cleaned and upgraded at random times - but what is here works.

Pi-send_characters_to_MegaLCD_a.c

// /home/graham/UTFT_display/Pi-send_characters_to_MegaLCD_a.c

// Send any length text string to x/y coordinate on Mega LCD

// to compile use gcc Pi-send_characters_to_MegaLCD_a.c -o Pi-send_characters_to_MegaLCD_a

// /home/graham/UTFT_display/Pi-Ard_send_9_characters_to_MegaLCD_a 5 2 0 0 1 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 - fill the top line (red)


#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>

int i2c;
int rc;
unsigned char data[50]; // the received byte store
unsigned long address; // the i2c bus address of the Arduino
int combined1;
int combined2;
int z;
int i;

int main(int argc, char *argv[])
{
printf("argc = %d\n", argc);
z = 12;
// check for correct input structure
//if (argc != (z+3)) {
///* error if we are not getting just (z + 3) inputs after the program name */
//fprintf(stderr, "usage: %s <address> <command_byte> <xx> <yy> <cc> <data-4> <data-5> .......<data-(z-3)>\n",argv[0]);
//exit(1);
//}

// --------------------------------------------------------the arduino i2c address
/* address is the first number after the program name */
address = atoi(argv[1]);
printf("address = %d\n", address);

// ----------------------------------------------------------the command byte
/* the second byte to send out to the arduino is the second number
place it into the first element of the data array - the command byte */
data[0] = atoi(argv[2]);
printf("command = %d\n", data[0]);

for (i=1; i <= (argc - 3); i++){
data[i] = atoi(argv[i+2]);
printf("data[] = %d\n", data[i]);
}
// ----------------------------------------------------------------------------

printf("I2C: Connecting\n");

// use /dev/i2c-0 for Pi model B, /dev/i2c-1 for B+ and 2
i2c = open("/dev/i2c-1",O_RDWR); /* open the device dev/i2c-1 */

printf("I2C: acquiring i2c address 0x%x\n", address);

rc=ioctl(i2c,I2C_SLAVE,address); /* set the i2c chip address */

write(i2c, data, (argc-2)); //send (z+1) bytes on the i2c bus

// As we are not talking to direct hardware but a microcontroller we
// need to wait a short while so that it can respond.
//
// 1ms seems to be enough but it depends on what workload it has
usleep(10000);

// Now wait else you could crash the arduino by sending requests too fast (not tested)
usleep(10000);

close(i2c);
return(0);
}


MegSlaveLcd-text-2.ino

// MegSlaveLcd-text-2
// drive the LCD colour display

// /home/graham/UTFT_display/Pi-send_characters_to_MegaLCD_a 5 2 0 0 1 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 - fill the top line (red)

#include <Wire.h>

#include <UTFT.h>
// - - - - - - - - - - - - - - - - - - - - - - -load some fonts
// extern uint8_t SmallFont[];
// extern uint8_t BigFont[];
extern uint8_t GroteskBold24x48[];
UTFT myGLCD(CTE32HR,38,39,40,41); // for my particular board

#define SLAVE_ADDRESS 0x05

int number = 0;
int command = 0;
byte data[30];
char datachar[30];
word xxword;

word combined1 = 0; //- - word or double needed here - int fails (why??)
word combined2 = 0;
word combined3 = 0;
int x;
int s = 0;
int c = 0;
int z;
int xx; // x coordinate
int yy; // y coordinate
int cc; // my colour code
int red;
int green;
int blue;
int r;
int g;
int b;
double temp;
word send_to_Pi = 0;
float temp0x10;
float temperature0;
float temp1x10;
float temperature1;
float temp2x10;
float temperature2;

//=====================================================================setup START
void setup() {

myGLCD.InitLCD();
myGLCD.clrScr();
pinMode(13, OUTPUT);

// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);

// define callbacks for i2c communication
Wire.onReceive(receiveData);
Wire.onRequest(sendData);
Serial.begin(9600); // open the serial port at 9600 bps:
}
//=====================================================================setup END

//---------------------------------------------------------------------------------loop START
void loop() {


delay (700);
myGLCD.setBackColor(0, 0, 0);
myGLCD.setFont(GroteskBold24x48);

Flash_LED_13(); //show we are alive

if (command==1) { // **************************************************************************command = 1
Serial.println("command is 1 ");
c = c + 1;
Serial.println (c);
if (c == 20)
{
myGLCD.clrScr(); //clean up scattered numbers
c = 0;
}

//----------------------------------------------------------------------------drive the LCD
if (s == 1)
{
myGLCD.setColor(255, 255, 255);
}
else
{
myGLCD.setColor(255, 0, 0);
}

myGLCD.fillCircle(20,20,15);

myGLCD.setColor(255, 110, 110);
myGLCD.printNumF(temperature1, 1, 370, 0, '.', 3); // for floats 1 digit after decimal point
// reserve space for 3 digits
myGLCD.print("garden =",140,0); // for strings

myGLCD.setColor(0, 255, 0);
myGLCD.printNumF(temperature0, 1, 370, 50, '.', 3);
myGLCD.print("greenhouse_S =",LEFT,50);

myGLCD.setColor(150, 150, 255);
myGLCD.printNumF(temperature2, 1, 370, 100, '.', 3);
myGLCD.print("greenhouse_W =",LEFT,100);

delay (700);
myGLCD.setColor(0, 0, 255);
myGLCD.fillCircle(20,20,15);

}

if (command==2) { // **************************************************************************command = 2
red = 0;
green = 0;
blue = 0;
cc = data[3];

if (cc == 1) {
red = 255;
}

if (cc == 2) {
green = 255;
}

if (cc == 3) {
blue = 255;
}

Serial.print ("command byte 2 received, send_to_Pi is ->");

Serial.println (send_to_Pi);
Serial.print ("byteCount = ");
Serial.println (z);
Serial.print ("colour cc = ");
Serial.println (cc);
Serial.print ("red = ");
Serial.println (red);
Serial.print ("green = ");
Serial.println (green);
Serial.print ("blue = ");
Serial.println (blue);

xx = data[1];
xx = 2*xx;
yy = data[2];
yy = 2*yy;
Serial.print ("xx = ");
Serial.println (xx);
Serial.print ("yy = ");
Serial.println (yy);

// myGLCD.clrScr(); //clean up scattered numbers

myGLCD.setColor(red, green, blue);
// myGLCD.printNumF(temperature1, 1, 370, 0, '.', 3); // for floats 1 digit after decimal point
// reserve space for 3 digits
data[z + 1] = 0; // (note - z = byteCount)

for (int i=0; i <= (z-1); i++){
data[i] = data [i+4]; // the first 3 bytes are command xx yy cc - pull the later bytes down to start at 0
datachar[i] = data[i]; //convert byte data to char data as needed by yGLCD.print
data [i+4] = 0;
datachar[i+4] = 0;
Serial.print ("data[i] = ");
Serial.println (data[i]);
}

myGLCD.print(datachar,xx,yy); // for strings
// myGLCD.print(" ",0,0);

command=0;
}
if (command==3) { // **************************************************************************command = 3 - clear the screen
myGLCD.clrScr(); //clean up scattered numbers
}

if (command==4) { // **************************************************************************command = 4 - set background color
// use /home/graham/UTFT_display/Pi-send_characters_to_MegaLCD_a 5 4 255 0 0 (red screen)
myGLCD.fillScr(r,g,b); // fill the screen with color
}

} //--------------------------------------------------------------------------------------loop END

//______________________________________________________________________________________________ receive data START
// in this receiveData section just save data then work on them in main loop
void receiveData(int byteCount){ //the incoming i2c bytes interrupt the processor

//get data from i2c
x=0;
while(Wire.available()) {
data[x] = Wire.read(); // (any number of bytes can be received if data[] is made large enough to hold them)
x++;
}
z = byteCount;

// §§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§ what is the command byte?
//++++++++++++++++++++++++++++++++++ data[0]=1 // use later
if (data[0] == 1){
command = 1;
send_to_Pi = 11; //use later?
s=1; //flash the white circle in loop
}

//++++++++++++++++++++++++++++++++++ data[0]=2 - display data string at xx/yy
if(data[0] == 2) {
command = 2;
send_to_Pi = 22;
}

//++++++++++++++++++++++++++++++++++ data[0]=3 - clear the screen
if(data[0] == 3) {
command = 3;
send_to_Pi = 33;
}
//++++++++++++++++++++++++++++++++++ data[0]=4 - colour the whole screen (not the text backgrounds that write on it)
if(data[0] == 4) {
command = 4;
r = data[1];
g = data[2];
b = data[3];
send_to_Pi = 44;
}
// §§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§
/*
combined1 = word(data[2],data[1]); //high-data[2] and low-data[1] bytes combine here
temp0x10 = combined1;
temperature0 = temp0x10/10;
temperature0 = temperature0 - (20 - temperature0) * 1.5/20;

combined2 = word(data[4],data[3]);
temp1x10 = combined2;
temperature1 = temp1x10/10;
temperature1 = temperature1 - (20 - temperature1) * 1.6/20;

combined3 = word(data[6],data[5]);
temp2x10 = combined3;
temperature2 = temp2x10/10;
temperature2 = temperature2 - (20 - temperature2) * 1.5/20;
*/


//for more than 4 commands use 5, 6 etc for the 2nd argument of the Pi code - process in loop
}
//__________________________________________________________________________________________ receive data from Pi END

//_________________________________________________________ send data to Pi START - not needed for Mega display yet
void sendData(){
// enter as byte test[] = {high,low} to send 2 byte number (255 255 gives 65535)
// Pi can use int to contain this, Arduino needs word, not int, for 2 bytes (why??)
byte test[] = {highByte(send_to_Pi),lowByte(send_to_Pi)};
Wire.write(test,2);
}
//________________________________________________________________________________________________ send data to Pi END

//=============================================================== functions below this line

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> FUNCTION Flash_LED_13
void Flash_LED_13(){
digitalWrite(13, HIGH); // set the LED on
delay(300);
digitalWrite(13, LOW); // set the LED off
delay(300);
}


stringsformegaLCD.bas

this is the Blassic "command string" program

#!/usr/sbin/blassic

' cut & paste to test NB! text string cannot contain spaces, _ (underline) is used in the input and it gets changed to a space
' /home/graham/housekeeping/stringsformegaLCD.bas address command xx yy cc data
' /home/graham/housekeeping/stringsformegaLCD.bas 5 2 452 276 red B - - - - puts a red B in the bottom right corner of the LCD
' /home/graham/housekeeping/stringsformegaLCD.bas 5 2 0 0 green ABCDEFGHIJKLMNOPQRSTU - - - fills the top line in green
' /home/graham/housekeeping/stringsformegaLCD.bas 5 2 100 200 blue temp_=_1.2 - - - -text with spaces
' /home/graham/housekeeping/stringsformegaLCD.bas 5 3- - - - clear the screen

DIM a$(30)
DIM a1$(30)
DIM a1_as_bytes(30) ' 20 enough for the GroteskBold24x48 to fill a line - NB change if small font used later
DIM a1_as_strings$(30)

i2c_address$ = PROGRAMARG$(1)
command$ = PROGRAMARG$(2)
xx$ = PROGRAMARG$(3) ' x coordinate of top left of first character in the string
yy$ = PROGRAMARG$(4) ' y coordinate

colour$ = PROGRAMARG$(5) ' colour code 1=red 2=green 3=blue
IF (colour$ = "red") THEN cc$ = "1"
IF (colour$ = "green") THEN cc$ = "2"
IF (colour$ = "blue") THEN cc$ = "3"

a$ = PROGRAMARG$(6) ' line of text to print on LCD - - - - _ will be spaces in the LCD string

xx$ = STR$((VAL(xx$))/2) ' arguments xx and yy must be typed as even numbers up to 510
yy$ = STR$((VAL(yy$))/2) ' so x and y coordinates are on 2 pixels grid since (at present) we only send one byte (0-255) to define x/y

' process any length of string into bytes
NumberOfCharacters = LEN(a$)

PRINT "NumberOfCharacters = ", NumberOfCharacters
PRINT "a$ string = ", a$
PRINT "i2c_address$ = ", i2c_address$
PRINT "command$ = ", command$
PRINT "xx$ = ", xx$
PRINT "yy$ = ", yy$
PRINT "cc$ = ", cc$

aa$ = ""
NumberOfCharacters = LEN(a$)

' create the string of bytes separated by spaces to feed a string into Raspi C code program Pi-send_characters_to_MegaLCD_a
FOR i = 1 to NumberOfCharacters
a1$(i) = MID$(a$, i, 1)
IF a1$(i) = "_" THEN a1$(i) = " "
a1_as_bytes(i) = ASC(a1$(i))
a1_as_strings$(i) = STR$(a1_as_bytes(i))
aa$ = aa$ + " " + a1_as_strings$(i)
PRINT aa$
NEXT i

screen_C_driver$ = "/home/graham/UTFT_display/Pi-send_characters_to_MegaLCD_a " + i2c_address$ + " " + command$ + " " + xx$ + " " + yy$ + " " + cc$ + aa$
PRINT screen_C_driver$
PAUSE 100
SHELL screen_C_driver$ ' drop to the operating system to run the C code Pi-send_characters_to_MegaLCD_a
PAUSE 5
PRINT "Done!"

SYSTEM

Footnote!
I want to use the above with LM75 i2c thermometers - I found this script (thanks) -

#!/bin/bash
while true;
do
i2cget -y 1 0x4F 0x00 w |
awk '{printf("%.1f\n", (a=( \
(("0x"substr($1,5,2)substr($1,3,1))*0.0625)+0.1) \
)>128?a-256:a)}'
sleep 5
done

awk is magic to me - but it works - even for negative temperatures and seems to be able to resolve to 0.1 degrees.
Remove the \n in {printf("%.1f\n" to just output the number without the return


Test with some real data

- - - - - it's a bit cold this evening!


Use the awk magic to get LM75 i2c thermometer data (via long i2c line between house and greenhouse)

#!/bin/bash
# /home/graham/housekeeping/lm75_0x4F_once.sh
i2cget -y 1 0x4F 0x00 w |
awk '{printf("%.1f", (a=( \
(("0x"substr($1,5,2)substr($1,3,1))*0.0625)+0.1) \
)>128?a-256:a)}'


#!/bin/bash
# /home/graham/housekeeping/lm75_0x48_once.sh
i2cget -y 1 0x48 0x00 w |
awk '{printf("%.1f", (a=( \
(("0x"substr($1,5,2)substr($1,3,1))*0.0625)+0.1) \
)>128?a-256:a)}'


The Blassic control program drops down to system to get the data
then sends commands via C code to the Mega with LCD

#!/usr/sbin/blassic

' /home/graham/housekeeping/LM75ForMegaLCD.bas

' drop to system to run lm75_0x48_once.sh and lm75_0x4F_once.sh
' save temperatures to ramdisk - then bring them back as strings

LABEL StartAgain

SHELL "echo -n `/home/graham/housekeeping/lm75_0x48_once.sh`>/var/www/ramdisk/LM75_48.txt"
OPEN "/var/www/ramdisk/LM75_48.txt" FOR INPUT AS #1 : INPUT #1,LM75_48$ : CLOSE #1
If OldLM75_48$ <> LM75_48$ THEN OldLM75_48$ = LM75_48$ : GOSUB ColorMegaBackground

SHELL "echo -n `/home/graham/housekeeping/lm75_0x4F_once.sh`>/var/www/ramdisk/LM75_4F.txt"
OPEN "/var/www/ramdisk/LM75_4F.txt" FOR INPUT AS #1 : INPUT #1,LM75_4F$ : CLOSE #1
If OldLM75_4F$ <> LM75_4F$ THEN OldLM75_4F$ = LM75_4F$ : GOSUB ColorMegaBackground

PRINT LM75_48$
PRINT LM75_4F$

SendtoMega1$ = "/home/graham/housekeeping/stringsformegaLCD.bas 5 2 0 0 green Small_Ghs_=_" + LM75_48$
PRINT SendtoMega1$
SHELL SendtoMega1$ ' this Blassic program now runs the Blassic program above - and returns here after
PAUSE 2000

SendtoMega2$ = "/home/graham/housekeeping/stringsformegaLCD.bas 5 2 0 50 red Outside_=_" + LM75_4F$
PRINT SendtoMega2$
SHELL SendtoMega2$
PAUSE 4000

GOTO StartAgain

'----------------------------------------------------------------------------------------Subroutines
' wipe and rewrite the screen green when data changes (uses command = 4)
LABEL ColorMegaBackground
ColorMegaBackground$ = "/home/graham/housekeeping/stringsformegaLCD.bas 5 4 0 250 0" 'green background
SHELL ColorMegaBackground$
PAUSE 2000
RETURN

SYSTEM

 


Please email me if you want to swap notes

SUNSPOT HOME