Interfacing between python and Arduino with lcd display

I built a 3.5" LCD display to show CPU and GPU stats but it keeps glitching as shown in the pics below.

I have attached all the files and i am new to the forum so please help.

APCMonitor.ino (3.82 KB)


IMG-20200107-WA0002.jpg (2.71 KB)

Please make your images visible in your Post. See this Simple Image Posting Guide

Also, as your program is short please include it in your Post using code button </>

People are reluctant to open strange ZIP files.

How do you know that the problem is not caused by the program on your PC?


People are reluctant to open strange ZIP files.

For me, it is not reluctance. I just won’t.

The person who wants help needs to make it easy for others to help.

To me, unzipping a file is more “work” than I want to do. Call me lazy. I don’t care.
Post the photos inline.
Use code tags </>

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_TFTLCD.h> // Hardware-specific library
#include <MCUFRIEND_kbv.h>

#define LCD_CS A3 // Chip Select goes to Analog 3
#define LCD_CD A2 // Command/Data goes to Analog 2
#define LCD_WR A1 // LCD Write goes to Analog 1
#define LCD_RD A0 // LCD Read goes to Analog 0

#define LCD_RESET A4 // Can alternately just connect to Arduino's reset pin

//   D0 connects to digital pin 8  
//   D1 connects to digital pin 9  
//   D2 connects to digital pin 2
//   D3 connects to digital pin 3
//   D4 connects to digital pin 4
//   D5 connects to digital pin 5
//   D6 connects to digital pin 6
//   D7 connects to digital pin 7

// Assign human-readable names to some common 16-bit color values:
#define  BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

MCUFRIEND_kbv tft(A3, A2, A1, A0, A4);

String inputString = "";        // String for buffering the message
boolean stringComplete = false; // Indicates if the string is complete

void serialEvent() {
  //while (!Serial.available()) {}
  while (Serial.available()) {
    char inChar = (char);
    inputString += inChar;
    if (inChar == '|') {
      stringComplete = true;

void setup(void) {
  Serial.println(F("TFT LCD test"));


  uint16_t identifier = tft.readID();
  Serial.print("TFT size is "); Serial.print(tft.width()); Serial.print("x"); Serial.println(tft.height());
  tft.setCursor(140, 175);
  tft.setCursor(140, 215);
  tft.setCursor(80, 255);

void loop(void) {
  while (Serial.available()) {
    char inChar = (char);
    inputString += inChar;
    if (inChar == '|') {
      stringComplete = true;

  if (stringComplete) {
    // CPU
    int cpuStringStart = inputString.indexOf("C");
    int cpuStringLimit = inputString.indexOf("|");
    String cpuString = inputString.substring(cpuStringStart + 1, cpuStringLimit);
    tft.setCursor(95, 50);
    // GPU 1
    int gpu1StringStart = inputString.indexOf("G", cpuStringLimit);
    int gpu1StringLimit = inputString.indexOf("|", gpu1StringStart);
    String gpu1String = inputString.substring(gpu1StringStart + 1 ,gpu1StringLimit);
    tft.setCursor(195, 175);
    // GPU 2
    int gpu2StringStart = inputString.indexOf("F", gpu1StringLimit);
    int gpu2StringLimit = inputString.indexOf("|", gpu2StringStart);
    String gpu2String = inputString.substring(gpu2StringStart + 1 ,gpu2StringLimit);
    tft.setCursor(195, 215);
    // GPU 3
    int gpu3StringStart = inputString.indexOf("g", gpu2StringLimit);
    int gpu3StringLimit = inputString.indexOf("|", gpu3StringStart);
    String gpu3String = inputString.substring(gpu3StringStart + 1 ,gpu3StringLimit);
    tft.setCursor(205, 255);
    inputString = "";
    stringComplete = false;

Python code:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import time
import json
import serial

from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

def space_pad(number, length):
    Return a number as a string, padded with spaces to make it the given length

    :param number: the number to pad with spaces
    :param length: the specified length
    :returns: the number padded with spaces as a string

    number_length = len(str(number))
    spaces_to_add = length - number_length
    return (' ' * spaces_to_add) + str(number)

def get_local_json_contents(json_filename):
    Returns the contents of a (local) JSON file

    :param json_filename: the filename (as a string) of the local JSON file
    :returns: the data of the JSON file

        with open(json_filename) as json_file:
                data = json.load(json_file)
            except ValueError:
                print('Contents of "' + json_filename + '" are not valid JSON')
    except IOError:
        print('An error occurred while reading "' + json_filename + '"')

    return data

def get_json_contents(json_url):
    Return the contents of a (remote) JSON file

    :param json_url: the url (as a string) of the remote JSON file
    :returns: the data of the JSON file

    data = None

    req = Request(json_url)
        response = urlopen(req).read()
    except HTTPError as e:
        print('HTTPError ' + str(e.code))
    except URLError as e:
        print('URLError ' + str(e.reason))
            data = json.loads(response.decode('utf-8'))
        except ValueError:
            print('Invalid JSON contents')

    return data

def find_in_data(ohw_data, name):
    Search in the OpenHardwareMonitor data for a specific node, recursively

    :param ohw_data:    OpenHardwareMonitor data object
    :param name:        Name of node to search for
    :returns:           The found node, or -1 if no node was found
    if ohw_data['Text'] == name:
        # The node we are looking for is this one
        return ohw_data
    elif len(ohw_data['Children']) > 0:
        # Look at the node's children
        for child in ohw_data['Children']:
            if child['Text'] == name:
                # This child is the one we're looking for
                return child
                # Look at this children's children
                result = find_in_data(child, name)
                if result != -1:
                    # Node with specified name was found
                    return result
    # When this point is reached, nothing was found in any children
    return -1

def get_hardware_info(ohw_ip, ohw_port, cpu_name, gpu_name, gpu_mem_size):
    Get hardware info from OpenHardwareMonitor's web server and format it

    # Init arrays
    my_info = {}
    gpu_info = {}
    cpu_core_temps = {}

    ohw_json_url = 'http://' + ohw_ip + ':' + ohw_port + '/data.json'

    # Get data from OHW's data json file
    data_json = get_json_contents(ohw_json_url)

    # Get info for CPU
    cpu_data = find_in_data(data_json, cpu_name)

    #cpu_temps = find_in_data(cpu_data, 'Temperatures')
    cpu_load = find_in_data(cpu_data, 'CPU Total')

    # Look for temperature of 4 cores
    #for i in range(0, 6):
        #core_temp = find_in_data(cpu_temps, 'CPU Core #' + str(i + 1))

        # Remove '.0 °C' from end of value
        #temp_value = core_temp['Value'][:-5]

        #cpu_core_temps[i] = temp_value

    #my_info['cpu_temps'] = cpu_core_temps

    # Get CPU total load, and remove ".0 %" from the end
    cpu_load_value = cpu_load['Value'][:-4]

    my_info['cpu_load'] = cpu_load_value

    # Get info for GPU
    gpu_data = find_in_data(data_json, gpu_name)

    gpu_clocks = find_in_data(gpu_data, 'Clocks')
    gpu_load = find_in_data(gpu_data, 'Load')

    gpu_core_clock = find_in_data(gpu_clocks, 'GPU Core')
    gpu_mem_clock = find_in_data(gpu_clocks, 'GPU Memory')
    gpu_temp = find_in_data(find_in_data(gpu_data, 'Temperatures'), 'GPU Core')
    gpu_core_load = find_in_data(gpu_load, 'GPU Core')
    fan_rpm = find_in_data(find_in_data(gpu_data, 'Fans'), 'GPU')
    fan_percent = find_in_data(find_in_data(gpu_data, 'Controls'), 'GPU Fan')
    gpu_mem_percent = find_in_data(gpu_load, 'GPU Memory')

    # Calculate how many MB of GPU memory are used based on the percentage
    used_percentage = float(gpu_mem_percent['Value'][:-2])
    used_mb = int((gpu_mem_size * used_percentage) / 100)

    # Add GPU info to GPU object
    gpu_info['temp'] = gpu_temp['Value'][:-5]
    gpu_info['load'] = gpu_core_load['Value'][:-4]
    gpu_info['core_clock'] = gpu_core_clock['Value'][:-4]
    # Memory clock divided by 2 so it is the same as GPU-Z reports
    gpu_info['mem_clock'] = int(int(gpu_mem_clock['Value'][:-4]) )#/ 2)
    gpu_info['used_mem'] = used_mb
    gpu_info['fan_percent'] = fan_percent['Value'][:-4]
    gpu_info['fan_rpm'] = fan_rpm['Value'][:-4]

    # Add GPU info to my_info
    my_info['gpu'] = gpu_info

    return my_info

def main():
    # Get serial ports
    ports = list(

    # Load config JSON
    cd = os.path.join(os.getcwd(), os.path.dirname(__file__))
    __location__ = os.path.realpath(cd)
    config = get_local_json_contents(os.path.join(__location__, 'config.json'))

    # If there is only 1 serial port (so it is the Arduino) connect to that one
    if len(ports) > 1:
        # Connect to the port
        port = 'COM3'
        print('Only 1 port found: ' + port + '. Connecting to it...')
        ser = serial.Serial(port,9600,timeout=0.5)
        while True:
            # Get current info
            my_info = get_hardware_info(

            # Prepare CPU string
            #cpu_temps = my_info['cpu_temps']
            cpu = \
                space_pad(int(my_info['cpu_load']), 2) + '%' + ""
                #space_pad(int(cpu_temps[0]), 2) + 'C ' + \
                #space_pad(int(cpu_temps[1]), 2) + 'C ' + \
                #space_pad(int(cpu_temps[2]), 2) + 'C ' + \
                #space_pad(int(cpu_temps[3]), 2) + 'C ' + \
                #space_pad(int(cpu_temps[4]), 2) + 'C ' + \
                #space_pad(int(cpu_temps[5]), 2) + 'C '

            # Prepare GPU strings
            gpu_info = my_info['gpu']
            gpu1 = \
            	space_pad(int(gpu_info['load']), 2) + '% ' + \
            	space_pad(int(gpu_info['temp']), 2) + 'C ' + \
            	space_pad(int(gpu_info['used_mem']), 4) + 'MB'

            gpu2 = \
            	space_pad(int(gpu_info['fan_percent']), 2) + '% F ' + \
            	space_pad(int(gpu_info['fan_rpm']), 4) + 'RPM'

            gpu3 = \
            	space_pad(int(gpu_info['core_clock']), 4) + '/' + \
            	space_pad(int(gpu_info['mem_clock']), 4) + 'MHz'

            # Send the strings via serial to the Arduino
            ser.write(str("C" + cpu + "|G" + gpu1 + "|F" + gpu2 +
                      	  "|g" + gpu3 + "|").encode())
            # Wait until refreshing Arduino again

     	print('Number of ports is not 1, can\'t connect!')

if __name__ == '__main__':

config.json file :

  "ohw_ip": "localhost",
  "ohw_port": "8085",
  "cpu_name": "Intel Core i7-8700K",
  "gpu_name": "NVIDIA GeForce GTX 1080",
  "gpu_mem_size": 8192

I am really sorry i am new to all this.

Where did you come up with this?
If you are new to this, why didn't you follow an example?

The Arduino doesn't run Python so what else is in this project?
There are holes in this that needs to be filled.

Where did you come up with this?
If you are new to this, why didn't you follow an example?

The Arduino doesn't run Python so what else is in this project?
There are holes in this that needs to be filled.

I am new as in I am new here at the forum not the programming part.

As for the project these are the files and there is an open source application called OpenHardwareMonitor that runs a local web server with the CPU and GPU stats which I read with python and send it to the arduino via serial to display.

1.) Don't use "S"trings - char arrays are much better
2.) Send data in a packetized format and not in a String
3.) Use the Python package (pip installable) named pySerialTransfer to send your values from Python to a COM port
4.) Use the Arduino library (IDE libraries manager installable) named SerialTransfer to parse the data from Python

Doing the above should make your code a lot easier to read and debug. Once your code is simplified it'll be easier to spot and mitigate bugs

Aren't Strings easier to manipulate?

Aren't Strings easier to manipulate?

Yes. That's what causes problems for an Arduino.


Aren't Strings easier to manipulate?

Honestly, if you know what you're doing they aren't easier at all. Working with binary data is much, much easier...