2020年3月31日火曜日

rpi-clone 4 Rasbian Buster option

最近知りました。
これで、Busterへのupgradeが楽になった。

The examples below show a /boot partition smaller than recommended for the recent Rasbian Buster release.
rpi-clone version 2.0.21 adds the -p option so the /boot partition can be resized at the same time
the root partition is resized to the end of the disk.
If you upgraded Stretch to Buster and are running with a small /boot,
then for the clone to have a resized /boot, run:

$ rpi-clone -f -p 256M sda

2020年3月26日木曜日

Show weather reports Controlling MAX7219 8x8 Matrix LED with Raspberry Pi

MAX7219 制御の8x8 Matrix LEDに天気情報を流すPython scriptです。


[MAX7219_matrix_weather3.py]

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

import time
import argparse

from luma.led_matrix.device import max7219
from luma.core.interface.serial import spi, noop


import local_weather
import img_gen2


def text_get():

   text=local_weather.get_weather()
   return text

def main(n, block_orientation, rotate, inreverse):
    # create matrix device
    serial = spi(port=0, device=0, gpio=noop())
    device = max7219(serial, cascaded=n or 1, block_orientation=block_orientation,
                     rotate=rotate or 0, blocks_arranged_in_reverse_order=inreverse)

    text=text_get()
    image_list = img_gen2.text_img_gen(n, text)
    max_images = len(image_list)
    while True:
      for i in range(0, max_images):
         device.display(image_list[i].convert(device.mode))
         time.sleep(0.1)
#         time.sleep(0.5)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='matrix_demo arguments',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser.add_argument('--cascaded', '-n', type=int, default=4, help='Number of cascaded MAX7219 LED matrices')
#    parser.add_argument('--block-orientation', type=int, default=0, choices=[0, 90, -90], help='Corrects block orientation when wired vertically')
    parser.add_argument('--block-orientation', type=int, default=-90, choices=[0, 90, -90], help='Corrects block orientation when wired vertically')
#    parser.add_argument('--rotate', type=int, default=0, choices=[0, 1, 2, 3], help='Rotate display 0=0°, 1=90°, 2=180°, 3=270°')
    parser.add_argument('--rotate', type=int, default=2, choices=[0, 1, 2, 3], help='Rotate display 0=0°, 1=90°, 2=180°, 3=270°')
    parser.add_argument('--reverse-order', type=bool, default=False, help='Set to true if blocks are in reverse order')

    args = parser.parse_args()

    try:
        main(args.cascaded, args.block_orientation, args.rotate, args.reverse_order)
    except KeyboardInterrupt:
        pass
        
        

[img_gen2.py]

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

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

_WIDTH=8

def text_img_gen(n, text):

    text_len=len(text)
    image = Image.new('1', (text_len*8, 8))
    draw  = ImageDraw.Draw(image)
    font  = ImageFont.truetype("/home/pi/font/misakifont/misaki_gothic.ttf", 8, encoding='unic')
    draw.text((0,0), text, font=font, fill=255)

    image_list = horizontal_scroll(n, image)
    return image_list

def horizontal_scroll(n, image, padding=True):

        image_list = list()
        width = image.size[0]
        w, hight = image.size

        # Scroll into the blank image.
        if padding:
            for x in range(n*_WIDTH):
                section = image.crop((0, 0, x, hight))
                display_section = create_blank_image(n, hight)
                display_section.paste(section, (_WIDTH*n - x, 0, _WIDTH*n, hight))
                image_list.append(display_section)

        #Scroll across the input image.
        for x in range(8*n, width + 1):
            section = image.crop((x - _WIDTH*n, 0, x, hight))
            display_section = create_blank_image(n, hight)
            display_section.paste(section, (0, 0, _WIDTH*n, hight))
            image_list.append(display_section)

        #Scroll out, leaving the blank image.
        if padding:
            for x in range(width - (_WIDTH*n-1), width + 1):
                section = image.crop((x, 0, width, hight))
                display_section = create_blank_image(n, hight)
                display_section.paste(section, (0, 0, ((_WIDTH*n)-1) - (x - (width - ((_WIDTH*n)-1))), hight))
                image_list.append(display_section)

        #Return the list of images created
        return image_list

def create_blank_image(n, hight):
      return Image.new("RGB", (_WIDTH*n, hight))
      
      


[local_weather.py]

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

import re
import json
import requests

#import sys
#sys.dont_write_bytecode = True

def get_weather():
#  url = 'http://weather.livedoor.com/forecast/webservice/json/v1'
  url = 'https://weather.tsukumijima.net/api/forecast'    ### 2021/2/21 update
  # 東京のcityタグのid
  payload = {'city': '130010'}
  # 天気情報をjsonで取得
  try:
     data = requests.get(url, params = payload).json()
  except Exception as e:
#     return e
     return '天気情報がありません'


  # 天気情報をedit
  rt=''
  max_len=len(data['forecasts'])
# 今日、明日、(明後日)の情報
  for forecast in data['forecasts']:
     rt+=('{0} ({1}) '.format(forecast['date'], forecast['dateLabel']))
     rt+=(' {} '.format(forecast['telop']))
     if forecast['temperature'].get('max') is not None:
        rt+= ('最高気温 {}℃  '.format(forecast['temperature']['max']['celsius']))
        rt+= ('最高湿度 {}% '.format(forecast['temperature']['max']['fahrenheit']))
     if forecast['temperature'].get('min') is not None:
        rt+= (' 最低気温 {}℃  '.format(forecast['temperature']['min']['celsius']))
        rt+= (' 最低湿度 {}% '.format(forecast['temperature']['min']['fahrenheit']))
     rt += ('{}の天気は、{}でしょう '.format(forecast['dateLabel'], forecast['telop']))

  rt+=('以降が{}の天気概況 '.format(data['location']['city']))
  rt+=(' ' + data['description']['text'])
  rt=re.sub(r'\n|。|、', "", rt)
  return rt

if __name__ == '__main__':
   rt=get_weather()
   print(rt)