Im trying to establish my own Raspy-Installation with a light-control.
I am trying to get a script-code working, to enable the light-control-service.
I am getting a really simple error message, but i cant fix it on my own:
Traceback (most recent call last):
File "test.py", line 13, in <module>
bus.write_byte_data(0x39, 0x00 | 0x80, 0x03)
IOError: [Errno 5] Input/output error
I just want to show you the complete code. Is anyone able to help me? :)
from os import system
system("sudo killall pigpiod")
system("sudo pigpiod")
import RPi.GPIO as GPIO
import time
import pigpio
import smbus
bus = smbus.SMBus(1)
#Lichtsensor (Adresse 0x39) initialisieren
#Power On Mode
bus.write_byte_data(0x39, 0x00 | 0x80, 0x03)
bus.write_byte_data(0x39, 0x01 | 0x80, 0x02)
#kurze Pause
time.sleep(0.5)
#Lichtsensor auslesen
data = bus.read_i2c_block_data(0x39, 0x0c | 0x80, 2)
data1 = bus.read_i2c_block_data(0x39, 0x0e | 0x80, 2)
#Lichtdaten aufbereiten
ch0 = data[1] * 256 + data[0]
ch1 = data1[1] * 256 + data1[0]
#Lichtdaten ausgeben
print "Volles Spektrum (Infrarot+sichtbar) : %d lux" %ch0
print "Infrarot-Wert: %d lux" %ch1
print "sichtbares Licht-Wert : %d lux" %(ch0 - ch1)
#GPIO ports setzen und zuweisen
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
red = 17
green = 27
blue = 22
PIR = 5
GPIO.setup(red, GPIO.OUT)
GPIO.setup(green, GPIO.OUT)
GPIO.setup(blue, GPIO.OUT)
GPIO.setup(PIR, GPIO.IN)
pi = pigpio.pi()
#Variablen definieren
pi.set_PWM_dutycycle(red, 0)
pi.set_PWM_dutycycle(green, 0)
pi.set_PWM_dutycycle(blue, 0)
fade_in_delay = .1
fade_out_delay = .1
delay_time = 5
mycolor = blue
brightness = 200
min_light = 9
moment = 0
RUNNING = True
#Script starten
try:
while RUNNING:
data = bus.read_i2c_block_data(0x39, 0x0c | 0x80, 2)
data1 = bus.read_i2c_block_data(0x39, 0x0e | 0x80, 2)
ch0 = data[1] * 256 + data[0]
ch1 = data1[1] * 256 + data1[0]
light = ch0 - ch1
print "sichtbares Licht-Wert : %d lux" %(light)
if pi.read(PIR):
print "PIR = ON"
else:
print "PIR = OFF"
if moment > 1:
start = moment
else:
start = 0
if (pi.read(PIR) & (light < min_light)):
for x in range(start,brightness):
pi.set_PWM_dutycycle(mycolor, x)
time.sleep(fade_in_delay)
moment = x
while (pi.read(PIR) & (light < min_light)):
time.sleep(delay_time)
if not (pi.read(PIR) & (moment > 0)):
for x in range(brightness, 0, -1):
pi.set_PWM_dutycycle(mycolor, x)
time.sleep(fade_out_delay)
moment = 0
except KeyboardInterrupt:
RUNNING = False
I couldnt transform the usual tools to fix the problem i got by using google. Im also just a beginner in writing own python-scripts. Normally I´m looking for existing codes (like this) and get it done for my own use case.
I try to use a AD5270, 20KOhm SPI potentiometer for a personal project with my Raspberry Pi 3B+. I translate some Arduino libraries code to Python language but it looks like it won't work. I can't test if the potentiometer is good value configured so I read the resistor value and always get 0 from SPI register.
Problem : I don't know if my writing is well done because I can't probe the resistor. To know if it works, I read the register and always receive 0x00. Writing and/or reading won't work.
Expect : reading what I have written.
Hardware setup :
!SYNC = CE0 = PIN24
DIN = MISO = PIN19
SDO = MOSI = PIN21
SLCK = SCLK = PIN23
with 1uF ext capacitor and 3V3 supply (from Raspberry).
The datasheet of the AD5270 is available here.
Minimalist code following the datasheet that won't work :
spi = spidev.SpiDev()
spi.open(spi_bus, spi_device_select)
spi.max_speed_hz = 50000 # Datasheet p7
spi.mode = 1 # CPOL = 0, CPHA = 1 (Datasheet p7)
# Datasheet example
data = [0x1C, 0x03]
r = spi.xfer(data)
print(r)
data = [0x05, 0x00]
r = spi.xfer(data)
print(r)
data = [0x08, 0x00]
r = spi.xfer(data)
print(r)
data = [0xC0, 0x00]
r = spi.xfer(data)
print(r)
From your hardware setup:
DIN = MISO = PIN21 (through 10 Ohm resistor)
SDO = MOSI = PIN19 (through 10 Ohm resistor)
DIN is input to AD5270, and therefore output of your RPI, so it should be MOSI. Same problem for SDO.
I finally got the tricks. Don't forget to add a pullup on SDO line and configure Spidev for the AD5270. This code work quite well :
# SPI test code for AD5270BRMZ-20
import time
import spidev
spi_bus = 0 # SPI0
spi_device_select = 0 # CE0
spi = spidev.SpiDev()
spi.open(spi_bus, spi_device_select)
spi.max_speed_hz = 50000 # Datasheet p7
spi.mode = 1 # CPOL = 0, CPHA = 1 (Datasheet p7)
spi.lsbfirst = False # Datasheet p18
MAX_RESISTANCE = 20000.0
WRITE_CTRL_REG = 0x1C
READ_CTRL_REG = 0x20
WRITE_RDAC = 0x04
READ_RDAC = 0x08
RDAC_WRITE_PROTECT = 0x02
def AD5270_CalcRDAC(resistance):
return int((resistance / MAX_RESISTANCE) * 1024.0)
def AD5270_ReadReg(command):
data = [(command & 0x3C), 0]
r = spi.xfer2(data)
data = [0x00, 0x00]
r2 = spi.xfer2(data)
result = r2[0]
result = (result << 8) | r2[1]
return result
def AD5270_WriteReg(command, value):
ui16Command = (command & 0x3C) << 8 | (value & 0x3FF)
data = [(ui16Command >> 8) & 0xFF, ui16Command & 0xFF]
spi.xfer2(data)
def AD5270_ReadRDAC():
RDAC_val = AD5270_ReadReg(READ_RDAC)
RDAC_val &= 0x03FF
return ((RDAC_val) * MAX_RESISTANCE) / 1024.0
def AD5270_WriteRDAC(resistance):
RDAC_val = AD5270_CalcRDAC(resistance)
spi.xfer2([WRITE_CTRL_REG, RDAC_WRITE_PROTECT])
AD5270_WriteReg(WRITE_RDAC, RDAC_val);
return ((RDAC_val * MAX_RESISTANCE) / 1024.0)
while(1):
AD5270_WriteRDAC(10000.0) # Write 10KOhm
print(AD5270_ReadRDAC())
time.sleep(1)
spi.close()
My goal is to use dual RFID RC522 readers with Orange PI.
So far, I have managed to make only one working. (reading google, armbian and orange pi forums). Here is what I have done so far:
Hardware connection:
single RC 522
MOSI ——————————> pin 19
MISO ——————————-> pin 21
SCLK ——————————-> pin 23
SDA ——————————–> pin 24
RST ———————————> pin 22
IRQ ———————————-> NONE
Second reader uses shared pins, except SDA, it goes to pin 26 on orange PI
software:
Install python dev
apt-get install python-dev
Install orangepi_PC_gpio_pyH3 Library
git clone https://github.com/duxingkei33/orangepi_PC_gpio_pyH3.git
cd orangepi_PC_gpio_pyH3
python setup.py install
Install SPI-Py Library
git clone https://github.com/lthiery/SPI-Py.git
cd SPI-Py
python setup.py install
Install MFRC522-python
git clone https://github.com/rasplay/MFRC522-python.git
The tricky part is, MFRC522-python is made to work with RASPBERRY PI on orange pi, one guy offered a solution by modifying MFRC522.py
#import RPi.GPIO as GPIO
import pyA20.gpio as GPIO
import spi
import signal
class MFRC522:
NRSTPD = 22
MAX_LEN = 16
PCD_IDLE = 0x00
PCD_AUTHENT = 0x0E
PCD_RECEIVE = 0x08
PCD_TRANSMIT = 0x04
PCD_TRANSCEIVE = 0x0C
PCD_RESETPHASE = 0x0F
PCD_CALCCRC = 0x03
PICC_REQIDL = 0x26
PICC_REQALL = 0x52
PICC_ANTICOLL = 0x93
PICC_SElECTTAG = 0x93
PICC_AUTHENT1A = 0x60
PICC_AUTHENT1B = 0x61
PICC_READ = 0x30
PICC_WRITE = 0xA0
PICC_DECREMENT = 0xC0
PICC_INCREMENT = 0xC1
PICC_RESTORE = 0xC2
PICC_TRANSFER = 0xB0
PICC_HALT = 0x50
MI_OK = 0
MI_NOTAGERR = 1
MI_ERR = 2
Reserved00 = 0x00
CommandReg = 0x01
CommIEnReg = 0x02
DivlEnReg = 0x03
CommIrqReg = 0x04
DivIrqReg = 0x05
ErrorReg = 0x06
Status1Reg = 0x07
Status2Reg = 0x08
FIFODataReg = 0x09
FIFOLevelReg = 0x0A
WaterLevelReg = 0x0B
ControlReg = 0x0C
BitFramingReg = 0x0D
CollReg = 0x0E
Reserved01 = 0x0F
Reserved10 = 0x10
ModeReg = 0x11
TxModeReg = 0x12
RxModeReg = 0x13
TxControlReg = 0x14
TxAutoReg = 0x15
TxSelReg = 0x16
RxSelReg = 0x17
RxThresholdReg = 0x18
DemodReg = 0x19
Reserved11 = 0x1A
Reserved12 = 0x1B
MifareReg = 0x1C
Reserved13 = 0x1D
Reserved14 = 0x1E
SerialSpeedReg = 0x1F
Reserved20 = 0x20
CRCResultRegM = 0x21
CRCResultRegL = 0x22
Reserved21 = 0x23
ModWidthReg = 0x24
Reserved22 = 0x25
RFCfgReg = 0x26
GsNReg = 0x27
CWGsPReg = 0x28
ModGsPReg = 0x29
TModeReg = 0x2A
TPrescalerReg = 0x2B
TReloadRegH = 0x2C
TReloadRegL = 0x2D
TCounterValueRegH = 0x2E
TCounterValueRegL = 0x2F
Reserved30 = 0x30
TestSel1Reg = 0x31
TestSel2Reg = 0x32
TestPinEnReg = 0x33
TestPinValueReg = 0x34
TestBusReg = 0x35
AutoTestReg = 0x36
VersionReg = 0x37
AnalogTestReg = 0x38
TestDAC1Reg = 0x39
TestDAC2Reg = 0x3A
TestADCReg = 0x3B
Reserved31 = 0x3C
Reserved32 = 0x3D
Reserved33 = 0x3E
Reserved34 = 0x3F
serNum = []
def __init__(self,spd=1000000):
spi.openSPI(speed=spd)
# GPIO.setmode(GPIO.BOARD)
# GPIO.setup(22, GPIO.OUT)
# GPIO.output(self.NRSTPD, 1)
self.MFRC522_Init()
def MFRC522_Reset(self):
self.Write_MFRC522(self.CommandReg, self.PCD_RESETPHASE)
def Write_MFRC522(self,addr,val):
spi.transfer(((addr<<1)&0x7E,val))
def Read_MFRC522(self,addr):
val = spi.transfer((((addr<<1)&0x7E) | 0x80,0))
return val[1]
def SetBitMask(self, reg, mask):
tmp = self.Read_MFRC522(reg)
self.Write_MFRC522(reg, tmp | mask)
def ClearBitMask(self, reg, mask):
tmp = self.Read_MFRC522(reg);
self.Write_MFRC522(reg, tmp & (~mask))
def AntennaOn(self):
temp = self.Read_MFRC522(self.TxControlReg)
if(~(temp & 0x03)):
self.SetBitMask(self.TxControlReg, 0x03)
def AntennaOff(self):
self.ClearBitMask(self.TxControlReg, 0x03)
def MFRC522_ToCard(self,command,sendData):
backData = []
backLen = 0
status = self.MI_ERR
irqEn = 0x00
waitIRq = 0x00
lastBits = None
n = 0
i = 0
if command == self.PCD_AUTHENT:
irqEn = 0x12
waitIRq = 0x10
if command == self.PCD_TRANSCEIVE:
irqEn = 0x77
waitIRq = 0x30
self.Write_MFRC522(self.CommIEnReg, irqEn|0x80)
self.ClearBitMask(self.CommIrqReg, 0x80)
self.SetBitMask(self.FIFOLevelReg, 0x80)
self.Write_MFRC522(self.CommandReg, self.PCD_IDLE);
while(i<len(sendData)):
self.Write_MFRC522(self.FIFODataReg, sendData[i])
i = i+1
self.Write_MFRC522(self.CommandReg, command)
if command == self.PCD_TRANSCEIVE:
self.SetBitMask(self.BitFramingReg, 0x80)
i = 2000
while True:
n = self.Read_MFRC522(self.CommIrqReg)
i = i - 1
if ~((i!=0) and ~(n&0x01) and ~(n&waitIRq)):
break
self.ClearBitMask(self.BitFramingReg, 0x80)
if i != 0:
if (self.Read_MFRC522(self.ErrorReg) & 0x1B)==0x00:
status = self.MI_OK
if n & irqEn & 0x01:
status = self.MI_NOTAGERR
if command == self.PCD_TRANSCEIVE:
n = self.Read_MFRC522(self.FIFOLevelReg)
lastBits = self.Read_MFRC522(self.ControlReg) & 0x07
if lastBits != 0:
backLen = (n-1)*8 + lastBits
else:
backLen = n*8
if n == 0:
n = 1
if n > self.MAX_LEN:
n = self.MAX_LEN
i = 0
while i<n:
backData.append(self.Read_MFRC522(self.FIFODataReg))
i = i + 1;
else:
status = self.MI_ERR
return (status,backData,backLen)
def MFRC522_Request(self, reqMode):
status = None
backBits = None
TagType = []
self.Write_MFRC522(self.BitFramingReg, 0x07)
TagType.append(reqMode);
(status,backData,backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, TagType)
if ((status != self.MI_OK) | (backBits != 0x10)):
status = self.MI_ERR
return (status,backBits)
def MFRC522_Anticoll(self):
backData = []
serNumCheck = 0
serNum = []
self.Write_MFRC522(self.BitFramingReg, 0x00)
serNum.append(self.PICC_ANTICOLL)
serNum.append(0x20)
(status,backData,backBits) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE,serNum)
if(status == self.MI_OK):
i = 0
if len(backData)==5:
while i<4:
serNumCheck = serNumCheck ^ backData[i]
i = i + 1
if serNumCheck != backData[i]:
status = self.MI_ERR
else:
status = self.MI_ERR
return (status,backData)
def CalulateCRC(self, pIndata):
self.ClearBitMask(self.DivIrqReg, 0x04)
self.SetBitMask(self.FIFOLevelReg, 0x80);
i = 0
while i<len(pIndata):
self.Write_MFRC522(self.FIFODataReg, pIndata[i])
i = i + 1
self.Write_MFRC522(self.CommandReg, self.PCD_CALCCRC)
i = 0xFF
while True:
n = self.Read_MFRC522(self.DivIrqReg)
i = i - 1
if not ((i != 0) and not (n&0x04)):
break
pOutData = []
pOutData.append(self.Read_MFRC522(self.CRCResultRegL))
pOutData.append(self.Read_MFRC522(self.CRCResultRegM))
return pOutData
def MFRC522_SelectTag(self, serNum):
backData = []
buf = []
buf.append(self.PICC_SElECTTAG)
buf.append(0x70)
i = 0
while i<5:
buf.append(serNum[i])
i = i + 1
pOut = self.CalulateCRC(buf)
buf.append(pOut[0])
buf.append(pOut[1])
(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buf)
if (status == self.MI_OK) and (backLen == 0x18):
print "Size: " + str(backData[0])
return backData[0]
else:
return 0
def MFRC522_Auth(self, authMode, BlockAddr, Sectorkey, serNum):
buff = []
buff.append(authMode)
buff.append(BlockAddr)
i = 0
while(i < len(Sectorkey)):
buff.append(Sectorkey[i])
i = i + 1
i = 0
while(i < len(serNum)):
buff.append(serNum[i])
i = i +1
(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_AUTHENT,buff)
if not(status == self.MI_OK):
print "AUTH ERROR!!"
if not (self.Read_MFRC522(self.Status2Reg) & 0x08) != 0:
print "AUTH ERROR(status2reg & 0x08) != 0"
return status
def MFRC522_Read(self, blockAddr):
recvData = []
recvData.append(self.PICC_READ)
recvData.append(blockAddr)
pOut = self.CalulateCRC(recvData)
recvData.append(pOut[0])
recvData.append(pOut[1])
(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, recvData)
if not(status == self.MI_OK):
print "Error while reading!"
print "Got data size: "+str(backLen)
i = 0
if len(backData) == 16:
print "Sector "+str(blockAddr)+" "+str(backData)
def MFRC522_Write(self, blockAddr, writeData):
buff = []
buff.append(self.PICC_WRITE)
buff.append(blockAddr)
crc = self.CalulateCRC(buff)
buff.append(crc[0])
buff.append(crc[1])
(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE, buff)
if not(status == self.MI_OK) or not(backLen == 4) or not((backData[0] & 0x0F) == 0x0A):
status = self.MI_ERR
print str(backLen)+" backdata &0x0F == 0x0A "+str(backData[0]&0x0F)
if status == self.MI_OK:
i = 0
buf = []
while i < 16:
buf.append(writeData[i])
i = i + 1
crc = self.CalulateCRC(buf)
buf.append(crc[0])
buf.append(crc[1])
(status, backData, backLen) = self.MFRC522_ToCard(self.PCD_TRANSCEIVE,buf)
if not(status == self.MI_OK) or not(backLen == 4) or not((backData[0] & 0x0F) == 0x0A):
print "Error while writing"
if status == self.MI_OK:
print "Data writen"
def MFRC522_Init(self):
# GPIO.output(self.NRSTPD, 1)
self.MFRC522_Reset();
self.Write_MFRC522(self.TModeReg, 0x8D)
self.Write_MFRC522(self.TPrescalerReg, 0x3E)
self.Write_MFRC522(self.TReloadRegL, 30)
self.Write_MFRC522(self.TReloadRegH, 0)
self.Write_MFRC522(self.TxAutoReg, 0x40)
self.Write_MFRC522(self.ModeReg, 0x3D)
self.AntennaOn()
def GPIO_CLEEN(self):
GPIO.cleanup()
4 lines are commented (109 - 111, 357) and first line is replaced to use pyA20.gpio instead of RPi.GPIO.
After that, I run read.py and it works like a charm.
import MFRC522
import signal
continue_reading = True
MIFAREReader = MFRC522.MFRC522()
cardA = [5,74,28,185,234]
cardB = [83,164,247,164,164]
cardC = [20,38,121,207,132]
def end_read(signal, frame):
global continue_reading
continue_reading = False
print "Ctrl+C captured, ending read."
MIFAREReader.GPIO_CLEEN()
signal.signal(signal.SIGINT, end_read)
while continue_reading:
(status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
if status == MIFAREReader.MI_OK:
print "Card detected"
(status,backData) = MIFAREReader.MFRC522_Anticoll()
if status == MIFAREReader.MI_OK:
print "Card read UID: "+str(backData[0])+","+str(backData[1])+","+str(backData[2])+","+str(backData[3])+","+str(backData[4])
if backData == cardA:
print "is Card A"
elif backData == cardB:
print "is Card B"
elif backData == cardC:
print "is Card C"
else:
print "wrong Card"
That is the way to use the reader on ORANGE PI PC. I googled and read further, in order to use second reader I need to modify exact same lines that I commented in order it to work. It controls RC522 with that SDA PIN, chooses from which reader to read the data. I try to uncomment any of them, but errors appear. Looks like those use specific RPi.GPIO functions. My python knowledge is very basic. I try to find where exactly are described pins that are used, and failed. Tried just to replace that pin 24 with 26 in order to read data from second readed. So far no success.
I have very similar setup and have had similar problems.
It seems I managed to solve them, just last night! :)
My project involves:
Orange pi zero
12-13 RFID-RC522 modules (for now only 2)
SPI communication
since I will need lots of digital output pins I will use 12-13 shift register ICs (74HC595)
Approaches and problems:
connect together SCK, MISO and MOSI pins, pull RST high, control each SS line separately by modifying MFRC522 class. Didn't work, maybe my timings were off, should connect logic analyzer and see.
connect together SCK, MISO, MOSI AND SS pins, pull RST high, switch on/off Vcc or GND pins (power down all readers but one, read from that one, switch to next). Didn't work and presented very interesting situation. In some cases RFID module can read cards even with Vcc, or GND, or both pins DISCONNECTED!!! There is some significant leakage current from the signal pins. Here I tried demuxes, shift registers, line driver ICs, nothing worked.
Finally, approach that DOES work. Connect together SCK, MISO, MOSI AND SS pins, control RST pin. RST when pulled low powers down module. Pull it up to power-on. So, initially all RST pins are pulled low, then one-by-one are pulled high, small delay to allow module to boot up (I use 200ms), call MFRC522_Init() (not sure if necessary in each cycle, doesn't hurt I guess), perform read, pull RST low, switch to next module. Nice side effect of this approach is low power consumption: 2 powered-down modules draw 3.6mA, 1 on - 1 off draw 18mA.
Hope this helps! :)
It was been a while, i had learn a little, made two RC522 readers to work on ESP8266, but same scenario can be used on ORANCE PI, in my previous attempt I was trying to make them work in separate SPI interface, now I use one interface and control them with SS (SDA) signal, when is send LOW on it, reader is active for communication. That way can be used more than one and control them that way. Hope that help to someone who seek an answers here :)
I am writing code that reads a file and sends it to an 20x4 LCD and I have buttons so I can move up and down the file
`
import math
import time
import linecache
import Adafruit_CharLCD as LCD
lcd = LCD.Adafruit_CharLCDPlate()
lcd.set_backlight (1)
end = 0
page = 0
load = 0
lineno = 0
txtfilename = ('file.txt' % spot)
while (end != 2):
if lcd.is_pressed(LCD.UP) or lcd.is_pressed(LCD.DOWN):
lcd.clear()
lineno += 1 if lcd.is_pressed(LCD.UP) else -1
text1 = linecache.getline(txtfilename, lineno)
text2 = linecache.getline(txtfilename, lineno +1)
text3 = linecache.getline(txtfilename, lineno +2)
text4 = linecache.getline(txtfilename, lineno +3)
print lineno
lcd.message(("%s%s%s%s") % (text1, text2, text3, text4))
print "%s" % text1
time.sleep (0.5)
end = 0
elif lcd.is_pressed(LCD.SELECT):
lcd.clear()
lcd.message ("Press agine to quit")
end +=1
#load
time.sleep (0.5)
lcd.clear()
lcd.message ('Goodbye')
time.sleep (0.5)
lcd.clear()
lcd.set_backlight (0)
`
The file that I will be reading will have lots of lines in it but each line will be under the 20 character
When I run this the 1st 4 lines are displayed but then I get the following
File "spot.py", line 54, in <module>
lcd.message(("%s%s%s%s") % (text1, text2, text3, text4))
File "/home/pi/me/Adafruit_CharLCD.py", line 246, in message
self.set_cursor(col, line)
File "/home/pi/me/Adafruit_CharLCD.py", line 182, in set_cursor
self.write8(LCD_SETDDRAMADDR | (col + LCD_ROW_OFFSETS[row]))
IndexError: tuple index out of range
I have seen other posts like this problem but I can't get them to work. Can anyone help me please?
Here is the Adafruit code I am using
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import time
import Adafruit_GPIO as GPIO
import Adafruit_GPIO.I2C as I2C
import Adafruit_GPIO.MCP230xx as MCP
import Adafruit_GPIO.PWM as PWM
# Commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80
# Entry flags
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00
# Control flags
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00
# Move flags
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00
# Function set flags
LCD_8BITMODE = 0x10
LCD_4BITMODE = 0x00
LCD_4LINE = 0x54
LCD_3LINE = 0x14
LCD_2LINE = 0x40
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00
# Offset for up to 4 rows.
LCD_ROW_OFFSETS = (0x00, 0x40, 0x14, 0x54)
# Char LCD plate GPIO numbers.
LCD_PLATE_RS = 15
LCD_PLATE_RW = 14
LCD_PLATE_EN = 13
LCD_PLATE_D4 = 12
LCD_PLATE_D5 = 11
LCD_PLATE_D6 = 10
LCD_PLATE_D7 = 9
LCD_PLATE_RED = 6
LCD_PLATE_GREEN = 7
LCD_PLATE_BLUE = 8
# Char LCD plate button names.
SELECT = 0
RIGHT = 1
DOWN = 2
UP = 3
LEFT = 4
class Adafruit_CharLCD(object):
"""Class to represent and interact with an HD44780 character LCD display."""
def __init__(self, rs, en, d4, d5, d6, d7, cols, lines, backlight=None,
invert_polarity=True,
enable_pwm=False,
gpio=GPIO.get_platform_gpio(),
pwm=PWM.get_platform_pwm(),
initial_backlight=1.0):
"""Initialize the LCD. RS, EN, and D4...D7 parameters should be the pins
connected to the LCD RS, clock enable, and data line 4 through 7 connections.
The LCD will be used in its 4-bit mode so these 6 lines are the only ones
required to use the LCD. You must also pass in the number of columns and
lines on the LCD.
If you would like to control the backlight, pass in the pin connected to
the backlight with the backlight parameter. The invert_polarity boolean
controls if the backlight is one with a LOW signal or HIGH signal. The
default invert_polarity value is True, i.e. the backlight is on with a
LOW signal.
You can enable PWM of the backlight pin to have finer control on the
brightness. To enable PWM make sure your hardware supports PWM on the
provided backlight pin and set enable_pwm to True (the default is False).
The appropriate PWM library will be used depending on the platform, but
you can provide an explicit one with the pwm parameter.
The initial state of the backlight is ON, but you can set it to an
explicit initial state with the initial_backlight parameter (0 is off,
1 is on/full bright).
You can optionally pass in an explicit GPIO class,
for example if you want to use an MCP230xx GPIO extender. If you don't
pass in an GPIO instance, the default GPIO for the running platform will
be used.
"""
# Save column and line state.
self._cols = cols
self._lines = lines
# Save GPIO state and pin numbers.
self._gpio = gpio
self._rs = rs
self._en = en
self._d4 = d4
self._d5 = d5
self._d6 = d6
self._d7 = d7
# Save backlight state.
self._backlight = backlight
self._pwm_enabled = enable_pwm
self._pwm = pwm
self._blpol = not invert_polarity
# Setup all pins as outputs.
for pin in (rs, en, d4, d5, d6, d7):
gpio.setup(pin, GPIO.OUT)
# Setup backlight.
if backlight is not None:
if enable_pwm:
pwm.start(backlight, self._pwm_duty_cycle(initial_backlight))
else:
gpio.setup(backlight, GPIO.OUT)
gpio.output(backlight, self._blpol if initial_backlight else not self._blpol)
# Initialize the display.
self.write8(0x33)
self.write8(0x32)
# Initialize display control, function, and mode registers.
self.displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF
self.displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_2LINE | LCD_5x8DOTS
self.displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT
# Write registers.
self.write8(LCD_DISPLAYCONTROL | self.displaycontrol)
self.write8(LCD_FUNCTIONSET | self.displayfunction)
self.write8(LCD_ENTRYMODESET | self.displaymode) # set the entry mode
self.clear()
def home(self):
"""Move the cursor back to its home (first line and first column)."""
self.write8(LCD_RETURNHOME) # set cursor position to zero
self._delay_microseconds(3000) # this command takes a long time!
def clear(self):
"""Clear the LCD."""
self.write8(LCD_CLEARDISPLAY) # command to clear display
self._delay_microseconds(3000) # 3000 microsecond sleep, clearing the display takes a long time
def set_cursor(self, col, row):
"""Move the cursor to an explicit column and row position."""
# Clamp row to the last row of the display.
if row > self._lines:
row = self._lines - 1
# Set location.
self.write8(LCD_SETDDRAMADDR | (col + LCD_ROW_OFFSETS[row]))
def enable_display(self, enable):
"""Enable or disable the display. Set enable to True to enable."""
if enable:
self.displaycontrol |= LCD_DISPLAYON
else:
self.displaycontrol &= ~LCD_DISPLAYON
self.write8(LCD_DISPLAYCONTROL | self.displaycontrol)
def show_cursor(self, show):
"""Show or hide the cursor. Cursor is shown if show is True."""
if show:
self.displaycontrol |= LCD_CURSORON
else:
self.displaycontrol &= ~LCD_CURSORON
self.write8(LCD_DISPLAYCONTROL | self.displaycontrol)
def blink(self, blink):
"""Turn on or off cursor blinking. Set blink to True to enable blinking."""
if blink:
self.displaycontrol |= LCD_BLINKON
else:
self.displaycontrol &= ~LCD_BLINKON
self.write8(LCD_DISPLAYCONTROL | self.displaycontrol)
def move_left(self):
"""Move display left one position."""
self.write8(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT)
def move_right(self):
"""Move display right one position."""
self.write8(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT)
def set_left_to_right(self):
"""Set text direction left to right."""
self.displaymode |= LCD_ENTRYLEFT
self.write8(LCD_ENTRYMODESET | self.displaymode)
def set_right_to_left(self):
"""Set text direction right to left."""
self.displaymode &= ~LCD_ENTRYLEFT
self.write8(LCD_ENTRYMODESET | self.displaymode)
def autoscroll(self, autoscroll):
"""Autoscroll will 'right justify' text from the cursor if set True,
otherwise it will 'left justify' the text.
"""
if autoscroll:
self.displaymode |= LCD_ENTRYSHIFTINCREMENT
else:
self.displaymode &= ~LCD_ENTRYSHIFTINCREMENT
self.write8(LCD_ENTRYMODESET | self.displaymode)
def message(self, text):
"""Write text to display. Note that text can include newlines."""
line = 0
# Iterate through each character.
for char in text:
# Advance to next line if character is a new line.
if char == '\n':
line += 1
# Move to left or right side depending on text direction.
col = 0 if self.displaymode & LCD_ENTRYLEFT > 0 else self._cols-1
self.set_cursor(col, line)
# Write the character to the display.
else:
self.write8(ord(char), True)
def set_backlight(self, backlight):
"""Enable or disable the backlight. If PWM is not enabled (default), a
non-zero backlight value will turn on the backlight and a zero value will
turn it off. If PWM is enabled, backlight can be any value from 0.0 to
1.0, with 1.0 being full intensity backlight.
"""
if self._backlight is not None:
if self._pwm_enabled:
self._pwm.set_duty_cycle(self._backlight, self._pwm_duty_cycle(backlight))
else:
self._gpio.output(self._backlight, self._blpol if backlight else not self._blpol)
def write8(self, value, char_mode=False):
"""Write 8-bit value in character or data mode. Value should be an int
value from 0-255, and char_mode is True if character data or False if
non-character data (default).
"""
# One millisecond delay to prevent writing too quickly.
self._delay_microseconds(1000)
# Set character / data bit.
self._gpio.output(self._rs, char_mode)
# Write upper 4 bits.
self._gpio.output_pins({ self._d4: ((value >> 4) & 1) > 0,
self._d5: ((value >> 5) & 1) > 0,
self._d6: ((value >> 6) & 1) > 0,
self._d7: ((value >> 7) & 1) > 0 })
self._pulse_enable()
# Write lower 4 bits.
self._gpio.output_pins({ self._d4: (value & 1) > 0,
self._d5: ((value >> 1) & 1) > 0,
self._d6: ((value >> 2) & 1) > 0,
self._d7: ((value >> 3) & 1) > 0 })
self._pulse_enable()
def create_char(self, location, pattern):
"""Fill one of the first 8 CGRAM locations with custom characters.
The location parameter should be between 0 and 7 and pattern should
provide an array of 8 bytes containing the pattern. E.g. you can easyly
design your custom character at http://www.quinapalus.com/hd44780udg.html
To show your custom character use eg. lcd.message('\x01')
"""
# only position 0..7 are allowed
location &= 0x7
self.write8(LCD_SETCGRAMADDR | (location << 3))
for i in range(8):
self.write8(pattern[i], char_mode=True)
def _delay_microseconds(self, microseconds):
# Busy wait in loop because delays are generally very short (few microseconds).
end = time.time() + (microseconds/1000000.0)
while time.time() < end:
pass
def _pulse_enable(self):
# Pulse the clock enable line off, on, off to send command.
self._gpio.output(self._en, False)
self._delay_microseconds(1) # 1 microsecond pause - enable pulse must be > 450ns
self._gpio.output(self._en, True)
self._delay_microseconds(1) # 1 microsecond pause - enable pulse must be > 450ns
self._gpio.output(self._en, False)
self._delay_microseconds(1) # commands need > 37us to settle
def _pwm_duty_cycle(self, intensity):
# Convert intensity value of 0.0 to 1.0 to a duty cycle of 0.0 to 100.0
intensity = 100.0*intensity
# Invert polarity if required.
if not self._blpol:
intensity = 100.0-intensity
return intensity
class Adafruit_RGBCharLCD(Adafruit_CharLCD):
"""Class to represent and interact with an HD44780 character LCD display with
an RGB backlight."""
def __init__(self, rs, en, d4, d5, d6, d7, cols, lines, red, green, blue,
gpio=GPIO.get_platform_gpio(),
invert_polarity=True,
enable_pwm=False,
pwm=PWM.get_platform_pwm(),
initial_color=(1.0, 1.0, 1.0)):
"""Initialize the LCD with RGB backlight. RS, EN, and D4...D7 parameters
should be the pins connected to the LCD RS, clock enable, and data line
4 through 7 connections. The LCD will be used in its 4-bit mode so these
6 lines are the only ones required to use the LCD. You must also pass in
the number of columns and lines on the LCD.
The red, green, and blue parameters define the pins which are connected
to the appropriate backlight LEDs. The invert_polarity parameter is a
boolean that controls if the LEDs are on with a LOW or HIGH signal. By
default invert_polarity is True, i.e. the backlight LEDs are on with a
low signal. If you want to enable PWM on the backlight LEDs (for finer
control of colors) and the hardware supports PWM on the provided pins,
set enable_pwm to True. Finally you can set an explicit initial backlight
color with the initial_color parameter. The default initial color is
white (all LEDs lit).
You can optionally pass in an explicit GPIO class,
for example if you want to use an MCP230xx GPIO extender. If you don't
pass in an GPIO instance, the default GPIO for the running platform will
be used.
"""
super(Adafruit_RGBCharLCD, self).__init__(rs, en, d4, d5, d6, d7,
cols,
lines,
enable_pwm=enable_pwm,
backlight=None,
invert_polarity=invert_polarity,
gpio=gpio,
pwm=pwm)
self._red = red
self._green = green
self._blue = blue
# Setup backlight pins.
if enable_pwm:
# Determine initial backlight duty cycles.
rdc, gdc, bdc = self._rgb_to_duty_cycle(initial_color)
pwm.start(red, rdc)
pwm.start(green, gdc)
pwm.start(blue, bdc)
else:
gpio.setup(red, GPIO.OUT)
gpio.setup(green, GPIO.OUT)
gpio.setup(blue, GPIO.OUT)
self._gpio.output_pins(self._rgb_to_pins(initial_color))
def _rgb_to_duty_cycle(self, rgb):
# Convert tuple of RGB 0-1 values to tuple of duty cycles (0-100).
red, green, blue = rgb
# Clamp colors between 0.0 and 1.0
red = max(0.0, min(1.0, red))
green = max(0.0, min(1.0, green))
blue = max(0.0, min(1.0, blue))
return (self._pwm_duty_cycle(red),
self._pwm_duty_cycle(green),
self._pwm_duty_cycle(blue))
def _rgb_to_pins(self, rgb):
# Convert tuple of RGB 0-1 values to dict of pin values.
red, green, blue = rgb
return { self._red: self._blpol if red else not self._blpol,
self._green: self._blpol if green else not self._blpol,
self._blue: self._blpol if blue else not self._blpol }
def set_color(self, red, green, blue):
"""Set backlight color to provided red, green, and blue values. If PWM
is enabled then color components can be values from 0.0 to 1.0, otherwise
components should be zero for off and non-zero for on.
"""
if self._pwm_enabled:
# Set duty cycle of PWM pins.
rdc, gdc, bdc = self._rgb_to_duty_cycle((red, green, blue))
self._pwm.set_duty_cycle(self._red, rdc)
self._pwm.set_duty_cycle(self._green, gdc)
self._pwm.set_duty_cycle(self._blue, bdc)
else:
# Set appropriate backlight pins based on polarity and enabled colors.
self._gpio.output_pins({self._red: self._blpol if red else not self._blpol,
self._green: self._blpol if green else not self._blpol,
self._blue: self._blpol if blue else not self._blpol })
def set_backlight(self, backlight):
"""Enable or disable the backlight. If PWM is not enabled (default), a
non-zero backlight value will turn on the backlight and a zero value will
turn it off. If PWM is enabled, backlight can be any value from 0.0 to
1.0, with 1.0 being full intensity backlight. On an RGB display this
function will set the backlight to all white.
"""
self.set_color(backlight, backlight, backlight)
class Adafruit_CharLCDPlate(Adafruit_RGBCharLCD):
"""Class to represent and interact with an Adafruit Raspberry Pi character
LCD plate."""
def __init__(self, address=0x20, busnum=I2C.get_default_bus(), cols=20, lines=4):
"""Initialize the character LCD plate. Can optionally specify a separate
I2C address or bus number, but the defaults should suffice for most needs.
Can also optionally specify the number of columns and lines on the LCD
(default is 20x4).
"""
# Configure MCP23017 device.
self._mcp = MCP.MCP23017(address=address, busnum=busnum)
# Set LCD R/W pin to low for writing only.
self._mcp.setup(LCD_PLATE_RW, GPIO.OUT)
self._mcp.output(LCD_PLATE_RW, GPIO.LOW)
# Set buttons as inputs with pull-ups enabled.
for button in (SELECT, RIGHT, DOWN, UP, LEFT):
self._mcp.setup(button, GPIO.IN)
self._mcp.pullup(button, True)
# Initialize LCD (with no PWM support).
super(Adafruit_CharLCDPlate, self).__init__(LCD_PLATE_RS, LCD_PLATE_EN,
LCD_PLATE_D4, LCD_PLATE_D5, LCD_PLATE_D6, LCD_PLATE_D7, cols, lines,
LCD_PLATE_RED, LCD_PLATE_GREEN, LCD_PLATE_BLUE, enable_pwm=False,
gpio=self._mcp)
def is_pressed(self, button):
"""Return True if the provided button is pressed, False otherwise."""
if button not in set((SELECT, RIGHT, DOWN, UP, LEFT)):
raise ValueError('Unknown button, must be SELECT, RIGHT, DOWN, UP, or LEFT.')
return self._mcp.input(button) == GPIO.LOW
The text you pass to lcd.message has four newlines in it due to being composed of four lines read from the file. But the LCD only has four lines, so it supports only text with up to three newlines. The fourth newline tries to move the cursor past the last row and causes the error. Remove the last newline (e.g. with .rstrip('\n')) to fix this.
msg = "%s%s%s%s" % (text1, text2, text3, text4)
lcd.message(msg.rstrip('\n'))
Edit: The library you're using tries to prevent this from being a problem even if you pass in too many newlines, but there's a bug in their check. In set_cursor:
# Clamp row to the last row of the display.
if row > self._lines:
row = self._lines - 1
The > should be >=.