Saturday 25 June 2016

PaPiRus Clock and Temperature

Optional Extras with PaPiRus

Following a bit of tinkering with the PaPiRus clock and temperature chips I thought I'd post about the implementation. 

I'm not claiming originality in this post as Frank Seliger has already posted about this. You can take a look at his blogger site to get more in-depth information. 

This is part of a larger project I'm building using the PaPiRus and the internal clock and temperature sensor took my interest. I found the implementation a little more challenging and thought I would share my experiences here.

Before trying this yourself, ensure that you have I2C enabled in raspi-config.
Run
> sudo raspi-config
from the command line and enable I2C.

PaPiRus LM75B Temperature Sensor

I'm a little confused as to why this was embedded into the PaPiRus. My only thoughts were that this is used or intended to be used to control the refresh of the e-ink display and probably manage the real time clock. 
I used Frank's shell script as an example as well as referring to the LM75B spec sheet. I'm not a great computer scientist or seasoned software engineer so I find myself banging my head against a wall whenever I have to convert MSB to LSB and potentially back again. The LM75B only returns a single 2 byte word for the temperature, but I don't think I've ever had so much trouble converting it. I have to admit that I'm a real newbie to Python and feel a bit more at home in C and C++.

Code Example

Let's get down to the Python code

import smbus
import time
from papirus import Papirus
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

SENSOR_ADDRESS = 0x48

def init_image(pap):
    # initially set all white background
    image = Image.new('1', pap.size, 1)
    return image

def write_papirus(img, text, size, x, y):
    # prepare for drawing
    draw = ImageDraw.Draw(img)

    fnt = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeMono.ttf', size)
     draw.text( (x, y) , text, font=fnt, fill=0)

def reverse_word_bytes(w):
     bitval = int(format(w,'016b')[-8:] + format(w, '016b')[:3], 2)
    if (format(bitval, '011b')[0] == '1'):
       bitval -=2048
    return bitval

def get_temp_celsius(bus):
    raw_temp = bus.read_word_data(SENSOR_ADDRESS,0x00)
    return reverse_word_bytes(raw_temp) * 0.125

bus = smbus.SMBus(1)
pir = Papirus()

first_run = True 

while True:
    temperature = get_temp_celsius(bus)

    print(u"Temp in \N{DEGREE SIGN}C = " + str(temperature))

     img = init_image(pir)

     write_papirus(img, "Temperature", 20, 0,0)
    write_papirus(img, format(temperature, '5.1f') + u'\N{DEGREE SIGN}C', 60, 0,50)

    pir.display(img)
    if first_run:
         pir.update()
        first_run = False
    else:
         pir.partial_update()

    time.sleep(5)

Implementation Notes

Partial updates are implemented in the example above so the display may show ghosting over time.
Temperatures seem to be on the high side. I've checked against the code that Frank produced and the temperatures are the same. I can only assume that the chip is heated from the electronics on board the PaPiRus and this creates a higher reading. 

This should work with negative temperatures, but I'm not risking the entire display by putting in a low temperature condition to test this. 

I couldn't get the LM75 spec examples to correlate with the output from the PaPiRus. I'm unsure if there's an error in the examples or down to the way the SMBus code converts to LSB. I've pulled the 11 bits as specified and reversed they bytes. Python's ability to convert to a string bit representation makes this conversion obvious. 

Copy the code to a file and run with python to get output to your PaPiRus display. Note that this assumes you've installed all git code correctly. See my earlier posts for reference to gratis and PaPiRus code libraries. 

Real Time Clock

This is a fairly standard setup for the MCP7941 clock module. Jessie distributions come with the drivers so there's not much to do.

First time setup

From the command prompt
> sudo modprobe i2c-dev
> sudo modprobe i2c:mcp7941x
> sudo bash

This creates a new shell with root permission
# echo mcp7941x 0x6f > /sys/class/i2c-dev/i2c-1/device/new_device
# logout

This should setup the hwclock. Confirm this with
> sudo hwclock -r

If this is the first setup then you will see an unset hardware clock outputting the wrong time.
Assuming you have your local clock setup with NTP then the final step is simple
> sudo hwclock -w

Manual setup of the clock can be done through the date command. Check the manual to see all settings
> man date

Start-up configuration

Ensure that the following kernel modules are loaded in /etc/modules file
i2c-dev
i2c:mcp7941x

Add them to the file if they are not already there. 

A few extra lines to the /etc/rc.local file should finish the setup
echo mcp7941x 0x6f > /sys/class/i2c-dev/i2c-1/device/new_device
hwclock --hctosys

Restarting your Raspberry Pi should keep using the hwclock over NTP. This should continue to work until the PaPiRus is disconnected. Ensure you updated the /etc/modules and /etc/rc.local file if you intend to permanently remove the PaPiRus display.