sudo python /home/pi/led_blink.py &
Команду sudo мы уже знаем, знак & говорит о том, что программа будет запущена в фоновом режиме. /home/pi/led_blink.py - это путь к программе, он может быть и другим. Кстати, узнать путь к текущему каталогу можно, введя команду pwd.
Сохранив файл, перезагрузим Paspberry Pi - мы увидим мигающий светодиод.
4.5. Используем I2C
Достаточно большое количество разнообразных устройств (экраны, датчики, АЦП) можно подключить к шине I2C. Но перед ее первым использованием, необходимо настроить Raspberry Pi.
Шаг-1. Запустить sudo raspi-config и активировать I2C, как показано на рисунке (это достаточно сделать только один раз):

После изменения настроек следует перезагрузить Raspberry Pi.
Шаг-2. Поставить программу i2c-tools, введя команду: sudo apt-get install i2c-tools
Теперь можно ввести команду и посмотреть доступные i2c-устройства:
sudo i2cdetect -y 1
Отобразится окно примерно такого вида:

Если ничего не было подключено, список будет пуст, иначе мы увидим номера подключенных устройств.
Шаг-3. Подключение устройства
Шина I2C использует 2 провода для передачи данных (пины “3” и “5” на Raspberry Pi), еще 2 будут нужны для “0” и питания. Пример для подключения I2C-дисплея показан на рисунке. Обратим внимание, что для питания устройства используется вывод “3.3В”.

После того как устройство подключено, следует еще раз набрать команду i2cdetect -y 1 и убедиться, что его адрес виден в списке. Без этого пытаться запустить какой-либо код бессмысленно.
4.6. Подключаем OLED-дисплей
Наличие дисплея безусловно актуально для многих устройств, так что и Raspberry Pi мы также не оставим без внимания. Тем более, что дисплей подойдет тот же самый, который мы уже рассматривали.

Подключим дисплей, как показано на предыдущей странице. Убедимся, что дисплей “виден” в системе, должен отображаться код 3С:

Теперь установим библиотеки, необходимые для работы дисплея. Введем следующие команды:
sudo apt-get install python-imaging python-smbus pillow
sudo apt-get install git
git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git
На этом шаге будет создан каталог Adafruit_Python_SSD1306. Зайдем в него и запустим установку библиотек для Python:
cd Adafruit_Python_SSD1306
sudo python setup.py install
Все готово. Проверить работу дисплея можно, запустив программу stats.py, находящуюся в папке examples:
sudo python examples/stats.py
Если все было сделано правильно, дисплей покажет информацию о системе:
Рассмотрим пример использования такого дисплея:
from PIL import Image, ImageDraw, ImageFont
import Adafruit_SSD1306
import time
disp = Adafruit_SSD1306.SSD1306_128_64(rst=None, i2c_address=0x3C)
# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, i2c_address=0x3C)
disp.begin()
# Load default font.
font = ImageFont.load_default()
# Create blank image for drawing. '1' - monochrome 1-bit color
image = Image.new('1', (disp.width, disp.height))
draw = ImageDraw.Draw(image)
value = 0
while True:
# Clear screen
draw.rectangle((0,0,width,height), outline=0, fill=0)
draw.text((x, top), "Hello world", font=font, fill=255)
draw.text((x, top+8), "Value "+ str(value), font=font, fill=255)
disp.image(image)
disp.display()
time.sleep(0.5)
Разберем код подробнее. Директивы import подключают необходимые библиотеки. Затем мы создаем в памяти объект disp класса Adafruit_SSD1306.SSD1306_128_64 (либо 128_32, если мы имеем такой дисплей). i2c_address - это адрес дисплея. Функция ImageFont.load_default, как нетрудно догадаться из названия, загружает в память шрифт. Затем, с помощью функции Image.new мы создаем в памяти монохромное изображение-буфер, в которое будем рисовать данные. Подготовленное изображение выводится с помощью метода disp.image(), метод disp.display() обновляет картинку на экране. Такая технология называется “двойной буфер” (double buffer), изображение сначала формируется в памяти, и только затем обновляется, это позволяет избежать мерцания.
Самостоятельная работа: изучить код примера examples/stats.py, в нем можно найти полезные функции, например для получения IP-адреса устройства. Исходный текст stats.py можно найти на github.
4.7. Подключаем кнопки
Раз уж мы подключили дисплей, остался последний шаг для создания автономного устройства на базе Raspberry Pi - добавить поддержку кнопок. Тем более, что для тех кто читал главу 3.2 про порты ввода-вывода на ESP32, все будет просто - принцип остается тот же.
Напомним общую идею, которая обсуждалась еще в первой части про Arduino - специальный резистор “подтягивает” напряжение до шины питания, а кнопка при необходимости замыкает его на землю:

На Raspberry Pi все то же самое, только резистор уже встроенный, и ставить его отдельно не надо. Ну и напряжение не 5В, а 3.3В, но для пользователя это ни на что не влияет. В итоге, схема получается проще некуда: просто подключаем кнопку, которая соединяет вывод с “землей”:
А вот код, в отличие от Arduino, будет немного отличаться.
Точнее говоря, есть два способа.
Способ-1. Код в стиле Arduino.
import RPi.GPIO as GPIO
import time
btn_pin = 24 # Button to GPIO24
# Setup
GPIO.setmode(GPIO.BCM)
GPIO.setup(btn_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
try:
# Loop
while True:
button_state = GPIO.input(btn_pin)
if button_state == False:
print 'Button Pressed...'
time.sleep(0.2)
except:
GPIO.cleanup()
Как можно видеть, здесь состояние кнопок читается в бесконечном цикле while True, и если значение кнопки равно логическому нулю, мы делаем какое-то действие.
Чем плох этот код? Он нормально работает на Arduino, т.к. там используется простой процессор, умеющий выполнять только одну задачу. Но на Raspberry Pi используется многоядерный процессор и полноценная многозадачная операционная система Linux, и постоянно работающий бесконечный цикл будет лишь зря забирать ресурсы процессора.
Гораздо правильнее “подписаться” на системные уведомления об изменении состояния кнопки, тогда код автоматически вызовется когда нужно.
Способ-2. Код с использованием событий (events).
import RPi.GPIO as GPIO
import time
btn_pin = 24 # Button to GPIO24
# Setup
GPIO.setmode(GPIO.BCM)
GPIO.setup(btn_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def btn_callback(channel):
if GPIO.input(btn_pin) != False:
print "Rising edge detected"
else:
print "Falling edge detected"
GPIO.add_event_detect(btn_pin, GPIO.RISING, callback=btn_callback, bouncetime=300)
# Loop
try:
while True:
time.sleep(1.0)
except:
GPIO.cleanup()
Разница значительна - здесь в основном цикле программы не делается ничего, кроме вызова time.sleep, такой код будет гораздо меньше нагружать процессор. Когда кнопка будет нажата, сработает функция btn_callback. Функция здесь только выводит сообщение, в реальном коде может обновляться содержимое дисплея или выполняться какое-то другое действие. Как можно видеть из кода, можно подписаться как на срабатывание кнопки (GPIO.RISING), так и на ее отпускание.
Разумеется, в реальном проекте может быть более одной кнопки, достаточно будет лишь добавить соответствующие вызовы GPIO.setup и GPIO.add_event_detect.
Самостоятельная работа: изучить код вывода текста на дисплей из предыдущей части, и объединив его с чтением состояния 4х кнопок, сделать систему меню. Простор для творчества тут большой, можно например использовать кнопки “вверх”, “вниз”, “OK” и “Cancel”.
4.8. Выходим в Web: запросы к серверу
Мы уже рассматривали получение количества друзей в “Контакте” в главе 3.6. Сделаем тот же самый запрос на языке Python. Здесь мы видим всю красоту и мощность данного языка, позволяющего делать вполне сложные вещи очень коротко и лаконично. Весь код умещается в 6 строк:
import json, urllib2
url = "https://api.vk.com/method/friends.get?user_id=29744451"
try:
data = json.load(urllib2.urlopen(url))
cnt = len(data['response'])
print cnt
except:
pass
Кстати, одна из полезных особенностей Python - его кроссплатформенность. Этот код можно выполнить и на Raspberry Pi, и на Windows, и на OSX, он будет работать одинаково.
Практически, подобные фрагменты удобно вынести в отдельную функцию, это делает код читабельнее и понятнее.
def getFriends(friendID):
url = "https://api.vk.com/method/friends.get?user_id=" + str(friendID)
try:
data = json.load(urllib2.urlopen(url))
cnt = len(data['response'])
return cnt
except:
return -1
Тогда вызвать его можно так:
n = getFriends(29744451)
print “Number of friends:”, n
Аналогично с числом подписчиков канала Youtube, все это можно записать в виде функции:
apiKey = "AIzaSyC26UJw-ubU6NXXXXXXXXXXXXXXXXXX"
def getSubscribersCount(channelID):
url = "https://www.googleapis.com/youtube/v3/channels?id=" + channelID + "&part=statistics&key=" + apiKey
try:
data = json.load(urllib2.urlopen(url))
return data["items"][0]["statistics"]["subscriberCount"]
except:
return -1
Может возникнуть вопрос, как мы получили строчку data["items"][0]["statistics"]["subscriberCount"]? Это просто, если посмотреть на json в браузере. Выглядит он напомним, так:
{
"kind": "youtube#channelListResponse",
"items": [
{
"kind": "youtube#channel",
"etag": "\"_gJQceDMxJ8gP-8T2HLXUoURK8c/Ea_ipJwsnrECB064UURA_RcRR0Y\"",
"id": "UCzz4CoEgSgWNs9ZAvRMhW2A",
"statistics": {
"viewCount": "30872448",
"commentCount": "313",
"subscriberCount": "258797",
"hiddenSubscriberCount": false,
"videoCount": "303"
}
}
]
}
Фигурными скобками обозначается элемент dictionary, доступ к элементам которого осуществляется через квадратные скобки - data["items"] вернет раздел items. Дальше смотрим на сам items: квадратные скобки в json обозначают массив, таким образом "items": [{... }] это массив из одного элемента типа dictionary, обратиться к нему можно как data["items"][0]. Дальше все аналогично, внутри есть еще один вложенный dictionary statistics, внутри него есть поле subscriberCount. Кстати, что такое dictionary, хорошо видно по полю statistics. Это так называемый “словарь” из парных элементов вида “имя”-”значение”, например { "viewCount": "30872448", "commentCount": "313", "subscriberCount": "258797" }.
Формат json сейчас является де-факто стандартом в web, за счет возможности описывать сложные иерархические структуры данных. Примеры разных файлов json желающие могут посмотреть на http://json.org/example.html. Поэтому при желании получать данные с различных веб-сервисов, нужно будет понимать как этот формат устроен.
Кстати, для отладки удобно выводить json прямо из Python, для этого достаточно стандартной функции print:
data = json.load(urllib2.urlopen(url))
print data["items"]
print data["items"][0]
print data["items"]["statistics"]
Так легко видеть, не ошиблись ли мы где-нибудь с полями данных.
4.9. Выходим в Web: запускаем свой сервер
Теперь рассмотрим обратную задачу - запустим свой сервер, который пользователь сможет открыть в браузере. Разумеется, Raspberry Pi имеет полноценную ОС Linux, и здесь можно запустить хоть Apache, хоть FTP. Но это не так интересно - мы запустим собственный сервер, который напишем на языке Python.
Для начала - самое простое, чего в 95% случаев будет вполне достаточно. Если мы хотим просто дать пользователю доступ к текущей папке, то в консоли достаточно ввести команду:
python -m SimpleHTTPServer 8000
Пользователь сможет зайти из браузера и увидеть (и скачать) любой файл:

Если нужно что-то посложнее, например управлять каким-либо устройством, придется написать свой код.
Рассмотрим минимальный пример работающего web-сервера на Python. Для примера, будем управлять светодиодом, как и в случае c ESP32. Но в отличие от ESP32, Raspberry Pi полноценно может работать с файлами, так что хранить HTML прямо в коде не требуется, просто сохраним файл как index.html.