Sunday 24 January 2021

Raspberry Pi Pico Microcontroller

 Raspberry Pi have just released a new product into a new market for them a low cost microcontroller. And not just a microcontroller using an existing piece of silicon, no, this is a in-house custom designed processor.

Why a microcontroller?

General purpose computers like the Raspberry Pi are great at doing lots of things, but that comes at a price. They can do lots of things that appear to humans  to be happening at the same time, but it is like juggling, if you are late getting to one of the items in the air, one or more of the items is going to come crashing down. By dedicating a microcontroller to the task, you can ensure the timely response.

Also, because microcontrollers do not have to contain all the clever hardware to handle lots of memory, task swapping and other things required for general purpose computers they are both economical in cost and energy requirements.

There are plenty of microcontrollers about such as the BBC Microbit and Arduino (and compatible), but most are built using an existing System On A Chip.

Raspberry Pi decided to build a dedicated microcontroller chip based on their experiences with the Sense Hat and the all in one Raspberry Pi 400.

What does Raspberry Pi Pico look like?


As you can see, it is much smaller than even the Raspberry Pi Zero (a WH example above). 

Out of the box it has only limited built in semsors and outputs, this is not a Circuit Playground Express.

Basically it has a green LED on GPIO pin 25 and a chip temperature sensor.

What is it for?

The Raspberry Pi Pico can be used where other microcontrollers would be used. It lacks the existing Arduino Shield eco-system, so generally where it is the basic I/O that is required.

It is small, and frugal with the power so it can run independently on two or three AA batteries.

How do you get one?

They are available from the usual suspects, I have one on order from Pimoroni.
In addition, they are on the cover of issue 39 of the print version of the Hackspace magazine.


What can you program it in?

At the moment there is a full C/C++ SDK and ports of Micropython and CircuitPython.
There is currently no Arduino implementation, but there will be an official RP2040 based Arduino device so I suspect that will not be a long wait.

Raspberry Pi RP2040

There is a full description of the RP2040 here, but these are the highlights:

  • In-house design using dual core ARM Cortex M0+
  • 264KB RAM
  • Upto 16MB of external Flash memory via a QSPI bus.
  • 30 GPIO pins (four owhich can be configured as analogue input)
  • Two each of UART, SPI and I2C controllers
  • 16 PWM channels
  • USB host and device support plus mass-storage boot mode for drag and drop programming
  • Eight Programmable I/O state machines

The eight PIO state machines are a particular innovation - they are programmable in a simple assembly language to perform tasks at set rates. Each instruction takes one cycle and is independent of the two main cores. This allows you to set up time sensitive operations at known speeds, irrespective of what the main processor is doing. Most microcontrollers would require bit-banging, using the processor to transmit or receive data by changing/reading the state of an input. The PIO allows this to be offloaded to a PIO with full control of the process. All the processor has to do is ensure that it is kept fed or emptied in time.

The chip is called an RP204 based on a naming system:

Cores: 2

CPU type: 0 ~ M0 - this is a loose description of the CPU type.

RAM: 4  = floor(log2(ram / 16k))

Flash: 0 = floor(log2(nonvolatile / 16k)) or zero in this case (the host board provides the Flash storage).

The Future

There are a range of products from Pimoroni, Adafruit, SparkFun and Arduino planned to use the RP2040, so we shall see.

References



Saturday 2 January 2021

Reading RSS feeds using Python

RSS Feeds

RSS stands for Really Simple Syndication and is a method of supplying information generally to be read by computers. An RSS feed is a web page and contains a number of entries, each entry commonly containing: title, link, description, publication date, and entry ID.

If you Google reading RSS in Python, you will often be directed to the feedparser library. This handles a lot of the background work involved in using an RSS feed and has a lot of protection against some of the potential pitfalls of reading what is effectively XML from potentially unknown sources. This includes only reading elements from a whitelist of trusted elements.

Unfortunately not all RSS feeds use elements on that whitelist.

One of those is the BBC RSS feeds. This is a problem in that I wanted to download the free BBC podcasts automatically.

So it was back to basics, the Requests library to access the RSS feed and download the files, and the defusedxml library to manipulate the RSS file as XML.

RSS files contain publication dates, so if you record the date of the last file you have downloaded, you can then use that date to only download new files when you retry the feed.

Installation

Remember to check that the version of pip is for Python 3.x (if not then replace pip with pip3)

Requests

pip install requests

Defusedxml

pip install defusedxml

Dateutil

This is required for date reading and comparison
pip install python-dateutil

Design

There are four main elements of the program:
  • Read the RSS file
  • Iterate through the new files
  • Download the podcasts
  • Update the last file date

Code

import defusedxml.ElementTree as ET
from pathlib import Path
import requests
from dateutil.parser import parse
import os

# Save the file refereenced by the URL to the supplied filepath
def saveMP3(url, filepath):
    print(filepath)
    r = requests.get(url)
    with open(filepath, 'wb') as f:
        f.write(r.content)

# Get all the files from the supplied RSS url that are newer than the 
# last file date stored in the matching file
def getFiles(url, target_folder, prefix):
    # Build the Namespace for the XML searching
    ns = {'media': "http://search.yahoo.com/mrss/"}

    # Create target folder if required
    if not Path(target_folder).is_dir():
        os.makedirs(target_folder)

    # Get response from URL
    r = requests.get(rss_url)
    print(r.status_code)

    # Make XML document from response text
    root = ET.fromstring(r.text)

    # Get the channel title
    channel_title = root.find("channel/title").text.replace(": ", "-")

    # Build the filename for the last downloaded file date
    channel_file = channel_title + "_lastdate.txt"
    print(channel_file)
    
    last_file_date = ""
    # Set up a default date if there is no file
    last_date = parse("2000-01-01 00:00:00 +00:00")

    # If there is a file, obtain the date from the file
    if Path(channel_file).is_file():
        f = open(channel_file, "r")
        last_file_date = f.read()
        last_date = parse(last_file_date)
        f.close()
    print(last_date)

    # Find the item elements from the XML
    items = root.findall("channel/item")

    print(channel_title + " files:" + str(len(items)))
    download_count = 0
    first_file_downloaded_date = ""

    # For each item found
    for y in items:
        # Obtain the information required
        title = y.find("title")
        link = y.find("media:content", ns)
        pub_date = y.find("pubDate")
        # Create the published date as datetime
        datetime_object = parse(pub_date.text)
        print(title.text)
        print(datetime_object)
        if last_file_date == "":              # Download if no previous date
            saveMP3(link.attrib['url'], target_folder+prefix+datetime_object.strftime("%Y%m%d%H%M%S") + ".mp3")
            download_count = download_count+1
        elif last_date < datetime_object:     # Download if newer than the last run
            saveMP3(link.attrib['url'], target_folder+prefix+datetime_object.strftime("%Y%m%d%H%M%S") + ".mp3")
            download_count = download_count+1
        else:                               # Otherwise exit loop
            print("Not downloading")
            break
        # Record the filedate of the first (most recent) file
        if first_file_downloaded_date == "":
            first_file_downloaded_date = pub_date.text
    
    # Print how many files and the most recent file date
    print("Downloaded " + str(download_count))
    print(first_file_downloaded_date)
    
    # Write the most recent file date to the file
    if first_file_downloaded_date != "":
        f = open(channel_file, "w")
        f.write(first_file_downloaded_date)
        f.close()

# If this is the main call
if __name__ == "__main__":
    rss_url = 'https://podcasts.files.bbci.co.uk/p02nrss1.rss'
    getFiles(rss_url, "/home/pi/Music/moreorless/","mol")

References