Below is some detail regarding the latest project I have been working on. Which is near completion.
I plan to wire it up permanently, place within a case and wall mount it.
Some of the images don’t come up too great in the video, I have included the actual images used here;
![]() | ![]() | ![]() |
![]() | ![]() |
I created a simple one line bash script that polls for the temperature every 5 mins.
The main python program uses pygame to write to the TFT via a framebuffer and Matplotlib is used to create the Graphs.
The display can be changed by either the button on the breadboard or via the remote FOB.
What is being displayed;
- Current Time.
- Last recorded temperature.
- Rotate between time and temperature.
- Graph of recorded temperature for the last 12 hours.
- Graph of recorded temperature for the last 24 hours.
- Graph of recorded temperature for the last 7 days.
To prevent the SD card filling up with data, when started, the user will be prompted to delete data older then 7 days.
[wp_ad_camp_4]
Hardware used
- Raspberry Pi Rev 2: http://www.raspberrypi.org
- 1.8″ TFT from Adafruit; http://www.adafruit.com/products/358
- 315 MHZ RF receiver and FOB from Adafruit; https://www.adafruit.com/products/1096
- Tactile switch from Adafruit; https://www.adafruit.com/products/1010 (I love these switches, they work flawlessly and have a good feel.)
- Temperature sensor from Sparkfun; https://www.sparkfun.com/products/9418
Wiring
Temperature logging
The script that records the temperature every 5 minutes;
while true; do echo $(i2cget -y 1 0x48 0x00 w | awk ‘{printf( “%.2f\n”, ( a=( “0x” substr( $1,5,2)substr($1,3,1))*0.0625 )>128?a-256:a)}’ )” “$(/bin/date +”%m-%d-%Y-%T”) ; /bin/sleep 300 ; done >> /home/pi/temperature_logging
Here is the code
#!/usr/bin/python from subprocess import * import matplotlib from numpy import genfromtxt matplotlib.use('Agg') import pylab import Image import pygame import os import time from time import strftime #from pygame.locals import* #from matplotlib.dates import DateFormatter import RPi.GPIO as GPIO import datetime def run_cmd(cmd): """Used to run Linux commands""" p = Popen(cmd, shell=True, stdout=PIPE) output = p.communicate()[0] return output def ckyorn( prompt): """Used for Y/N prompt""" while True: a=raw_input( prompt + " [y,n,?]: " ).upper() if a in [ 'Y', 'YES']: return True elif a in [ 'N', 'NO']: return False def delete_old_data(): """ Used to delete old data in input file. Will check for data older than 14 days and ask if you would like everything older than 7 days to be deleted. This should not being asked more than once a week""" now = datetime.datetime.now() #get the first line of the data file, which will be the oldest entry getfirst = run_cmd("head -1 temperature_logging | awk '{print $2}'") #convert string to time. firstLineTime = datetime.datetime.strptime(getfirst, '%m-%d-%Y-%H:%M:%S ') #Get the date and time for seven days ago from now. sevenDaysAgo = now - datetime.timedelta(days=7) sevenDaysAgoStr = sevenDaysAgo.strftime('%m-%d-%Y-%T') #If the data and time in the first line is older than 14 days, ask if okay to delete. if (now - firstLineTime) > datetime.timedelta(days=14): print ("More than 14 days worth of data has been collected.") if ckyorn ("Would you like to delete data older than 7 days?"): sedCommand = "sed -n -i '/" + sevenDaysAgoStr[1:15] + "/,$p' temperature_logging" run_cmd(sedCommand) def displayText(text, size, line, color, clearScreen): """Used to display text to the screen. displayText is only configured to display two lines on the TFT. Only clear screen when writing the first line""" if clearScreen: screen.fill((0, 0, 0)) font = pygame.font.Font(None, size) text = font.render(text, 0, color) textRotated = pygame.transform.rotate(text, -90) textpos = textRotated.get_rect() textpos.centery = 80 if line == 1: textpos.centerx = 90 screen.blit(textRotated,textpos) elif line == 2: textpos.centerx = 40 screen.blit(textRotated,textpos) def displayTime(): """Used to display date and time on the TFT""" screen.fill((0, 0, 0)) font = pygame.font.Font(None, 50) now=time.localtime() for setting in [("%H:%M:%S",60),("%d %b",10)] : timeformat,dim=setting currentTimeLine = strftime(timeformat, now) text = font.render(currentTimeLine, 0, (0,250,150)) Surf = pygame.transform.rotate(text, -90) screen.blit(Surf,(dim,20)) def graph(toKeep): """Used to display the graphs. Text will be shown before hand as the graphs take time to generate""" global firstTime imageFile = 'gp.png' #Display some text stating that graphs are being generated if toKeep == TwelveHours: displayText('Creating', 35, 1,(200,200,1),True) displayText('12 Hour Graph', 32, 2,(150,150,255), False) elif toKeep == TwentyFourHours: displayText('Creating', 35, 1,(200,200,1), True) displayText('24 Hour Graph', 32, 2,(150,150,255), False) elif toKeep == OneWeek: displayText('Creating', 35, 1,(200,200,1), True) displayText('One Week Graph', 28, 2,(150,150,255), False) pygame.display.flip() #Get temperature and time data from data file temp = genfromtxt(dataFile, dtype=None, usecols=(0), skip_header = lines - toKeep) timecaptured = genfromtxt(dataFile, dtype=None, usecols=(1), skip_header = lines - toKeep) #Site the size of the font for the axis for label in ax.get_xticklabels(): label.set_fontsize(8) for label in ax.get_yticklabels(): label.set_fontsize(8) #Create xaxis labels and only show every 12th or 96th xlabels = range(toKeep) if toKeep == OneWeek: pylab.xticks(xlabels[::96], [v[:5:] for v in timecaptured[::96]]) else: pylab.xticks(xlabels[::12], [v[11:16] for v in timecaptured[::12]]) #Plot the graph pylab.plot(temp,linewidth=2, antialiased=True) #Change some colours ax.patch.set_facecolor('#FFFFCC') ax.patch.set_alpha(0.5) #Rotate the text in the xaxis fig.autofmt_xdate(rotation=90) # ax.xaxis.set_major_formatter( DateFormatter('%H:%M') ) #Set the yaxsis limits pylab.ylim(0,40) #Save the graph as an image and the re-open it, rotate it and then display to TFT. pylab.savefig(imageFile, facecolor=fig.get_facecolor(),bbox_inches='tight', dpi=80,pad_inches=0.03) pil_im = Image.open(imageFile) pil_out = pil_im.rotate(-90) pil_out.save(imageFile) img = pygame.image.load(imageFile) screen.blit(img,(0,0)) pygame.display.flip() #Clear graph data in preparation for next plot pylab.cla() #Reset the firstTime Variable firstTime = 0 def main(): global ax,fig,screen global firstTime,lines global TwentyFourHours,TwelveHours,OneWeek global dataFile dataFile = 'temperature_logging' size = width, height = 128, 160 TFTxSize = 2.28 TFTySize = 1.63 TwentyFourHours = 288 TwelveHours = 144 OneWeek = 2016 firstTime = True #Used to work out if a function has already been run whatToDisplay = 1 #What do display on screen rotate = 1 #Used when automatically rotating the display. startTime = time.time() #Used to work out how much time has passed when rotating. #Set the framebuffer device to be the TFT os.environ["SDL_FBDEV"] = "/dev/fb1" #Setup pygame display pygame.init() pygame.mouse.set_visible(0) screen = pygame.display.set_mode(size) #Setup plot area fig = pylab.figure() ax = fig.add_subplot(111) fig.set_size_inches(TFTxSize, TFTySize) background = pygame.Surface(screen.get_size()) background = background.convert() #Setup GPIO GPIO.setmode(GPIO.BCM) GPIO.setup(17, GPIO.IN) #Read GPIO 17 as input #Open data file and count how many lines file = open(dataFile) lines = sum(1 for word in file if "." in word) file.seek(0,0) delete_old_data() while True: time.sleep(.5) """Check to see if button is pressed, and if so, increment by one or reset to one if over 6""" if GPIO.input(17): whatToDisplay = 1+(whatToDisplay%7) #Rotate between 1 and 6 firstTime = True #If button pressed, set this to True if whatToDisplay == 1: #Display time displayTime() elif whatToDisplay == 2: #Display last temperature recorded. file.seek(-26,2) #Go to end of data file to get temperature currentTemp = file.readline(5) + ' \xb0C' displayText('Current Temp', 30, 1, (200,200,1), True ) displayText(currentTemp, 50, 2, (150,150,255), False ) elif whatToDisplay == 4 and firstTime: graph(TwelveHours) elif whatToDisplay == 5 and firstTime: graph(TwentyFourHours) elif whatToDisplay == 6 and firstTime: graph(OneWeek) elif whatToDisplay == 3: #Rotate display elapsedTime = time.time() - startTime if elapsedTime > 5: rotate = not rotate startTime = time.time() if rotate: file.seek(-26,2) currentTemp = file.readline(5) + ' \xb0C' displayText('Current Temp', 30, 1, (200,200,1), True ) displayText(currentTemp, 50, 2, (150,150,255), False ) elif not rotate: displayTime() #Write to TFT pygame.display.flip() if __name__ == '__main__': main()
[wp_ad_camp_1]
Awesome project Mark!
I’ve been working with an Arduino Pro for the past couple of months, and having been an electronic technician in the past, this Open Hardware and software platform has re-ignited my passion for smelling burnt solder and getting hands-on once again.
Thank you for sharing your inspiring work!
Best,
Ron
I’m still a novice at these things; but should the resistor on the wiring diagram be on the line just above where it’s shown?
you are right. I have updated the diagram.
Thank you for that.
The schematic shows the pull down resistor for the switch also connected to the RF module ground. I think you meant for the ground to connect directly to the RF module.
Hi, thats a nice project. I’ve got the sainsmart display 1,8″. It seems to be the same as yours, but i can’t get it running. What I’ve changed in your code is to set the framebuffer to fb0 cause fb1 is not listed in /dev. I made some debug output and see that the code is running into the loop. But the display stays white. Its just flickering a bit when running in loop.
Your TFT appears to have the same controller. So it should work.
Check DMESG to see if your TFT is identified.;
pi@raspberrypi ~ $ dmesg | grep 7735
[ 2.401086] fb1: ST7735 frame buffer device,
Have you patch the kernel with the updated driver?
https://ozzmaker.com/2012/11/23/raspberrypi-tft/
I now have the sainsmart working following a previous instruction in your blog.
The only thing thats not working is the fbterm-login. It says “cannot execute fbterm-login” “ld respawning too fast. Disabled for 5 min” . Any idea for that?
I have never seen that error before.
Did you did it by creating /sbin/fbterm-login ?
what are the permissions on this file?
I think the 10K resistor is still in the wrong place, the RF receiver is not hooked to ground.
Thanks for the feedback Greg. I’m no expert on electronics, so any help is welcome.
I’m using the 10K resister to pull down GPIO 17. Where do you think it should be?
After taking a closer look, it can stay where it is, but the connections from the RF module do not make sense. GND does not go to GND, D0 goes to 3.3V and not GPIO 17.
Sorry Randy.. not sure why i called you Greg. 🙂
I know see what you mean, I have updated the diagram and it makes more sense now. Let me know what you think.
And again, thanks for your help.
Better, but now resistor is in wrong place. when the switch is pressed it will connect GND direct to 3.3V.
Corrected,
What program are you using to create the diagram, I want to start documenting my projects.
I use Microsoft Visio and save as JPG. Easy to draw lines in, plus I’m on it every day at work.
Hi,
I’ve the same error message like apohero on boot:
“cannot execute fbterm-login” “ld respawning too fast. Disabled for 5 min”
The permissions of /sbin/fbterm-login are “-rwxr-xr-x 1 root root 123 2013-03-10 20:05 fbterm-linux”
Can You please help me?
Are the contents of these files correct?
/sbin/fbterm-login
/etc/inittab
Hi,
here are the contents made with copy & paste:
/etc/inittab
…
# Note that on most Debian systems tty7 is used by the X Window System,
# so if you want to add more getty’s go ahead but skip tty7 if you run X.
#
#1:2345:respawn:/sbin/getty –noclear 38400 tty1
1:2345:respawn:/sbin/fbterm-login fb1 tty1
2:23:respawn:/sbin/getty 38400 tty2
3:23:respawn:/sbin/getty 38400 tty3
4:23:respawn:/sbin/getty 38400 tty4
5:23:respawn:/sbin/getty 38400 tty5
6:23:respawn:/sbin/getty 38400 tty6
…
/sbin/fbterm-login
#!/bin/bash
# example: fbterm-login fb1 tty1
export FRAMEBUFFER=/dev/$1
exec /usr/bin/fbterm -r 1 — /bin/login < /dev/$2
I just created an empty file for fbterm-login without any file ending, like .txt or something like that.
Do I have to give a file ending? Is the modification in the inittab correct?
that looks all correct.
Try this, revert /etc/inittab to how is was before.
Then, in this file;
/boot/cmdline.txt
place this text at the end;
fbcon=map:10 fbcon=rotate:1 fbcon=font:ProFont6x11
Reboot and see if the console is displayed on the TFT
Yes, that works fine. With fbcon everything’s ok, I can see the console on my display, but I don’t know how to get Images with fbcon on the display. Maybe you can give me a link, where I can read a manual, how fbcon works? That’s why I wanna use fbterm, because you mentioned the commands for fbterm in your tutorial.
Thanks a lot for your help!!! 🙂
Install FBI to display images on the TFT;
sudo fbi -T 1 -a -d /dev/fb1 filename
mplayer2 for video;
sudo mplayer -vo fbdev2:/dev/fb1 -x 128 -y 160 -zoom filename
links2 for web browser;
sudo links2 -g -mode 128x160x32k http://www.google.com
Hi,
thanks for your commands, but they are the same as in your tutorial. I thought that I can use these commands only with fbterm!? Can I use them in the same way with fbcon?
yeh sure… you could even use the programs if you don’t have fbterm or fbcon working.
I’m perplexed – the code above is for a 1.8″ (diagonal) display. In the code there are two lines:
TFTxSize = 2.28
TFTySize = 1.63
If I compute the diagonal based on x,y I come up with 2.8
What am I missing?
It has been awhile since I looked at this.
I do know that fig.set_size_inches is used to change the size of the plot.
I vaguely remember playing around with the two values above to get the plot to display at the correct aspect ratio. I should have named them something else as they have no relevance to the actual size of the TFT.