---

Friday, September 16, 2022

Simple Long Range Radio from Raspberry Pi to raspberry Pi Pico using HC-12 433MHz

I have a project in progress that is the third generation of my Hen House Door project. I want to be able to open and close the door from my home automation controller (Raspberry Pi 2 running NodeRED). Unfortunately, the hen house is at the end of the garden and out of WiFi range. Anyway the door opener is low-power and solar powered, and WiFi uses too much current.

I've used NRF24 and CC1101 modules before and frankly there are a lot of wires to connect for SPI and generally a mess of libraries to try and get working. Wouldn't it be lovely (I mused) if there was a long range wireless module that talked UART serial and hid all the communication layer stuff. 

With a bit of googling, I was delighted to find the HC-12 modules that do exactly that. They take AT commands to configure them for power mode etc. But aside from that they work just like you had a cable between your two devices and talked serial along it.

Eventually, my Raspberry Pi 2 will talk to the hen house controller that is based on an ATTiny1616 at 1MHz and using a minimal amount of electricity except when driving the door motor. But as a first test, I thought it would be good to establish a link between a Pico and a regular Raspberry Pi (in this case, my Pi 400).

The Pico

Here's the Pico end.


The connections are:

  • GND on the Pico to GND on the HC-12
  • 3V on the Pico to 3V on the HC-12
  • Tx on the Pico to Rx on the HC-12
  • Rx on the Pico to Tx on the HC-12
To test it out, I used the program below - making sure its copied onto the Pico itself (use Save a Copy in Thonny) so I can take it mobile with a battery pack.


Here's the code for ease of copying:

from machine import UART
import time

uart = UART(0, 9600, timeout=400)

count = 0

while True:
    count += 1
    print(count)
    uart.write(str(count)+"\n")
    time.sleep(1)


The Raspberry Pi

Here's an HC-12 attached to my Pi400 by way of a GPIO adapter and some jumper wires.


The connections are:

  • GND on the Pi to GND on the HC-12
  • 3V on the Pi to 3V on the HC-12
  • Tx on the Pi (14) to Rx on the HC-12
  • Rx on the Pi ( 15) to Tx on the HC-12
The receiving code is just:

import serial

ser = serial.Serial('/dev/serial0', baudrate=9600)

while True:
    line = ser.readline().decode("utf-8")
    print(line)

Run the program, and it should start receiving numbers from the Pico. Wander around with the Pico powered from a USB battery pack and see how far you can get before you start loosing data. 


pi@pi400:~ $ nano hc_12.py 

pi@pi400:~ $ python3 hc_12.py 

1553


1554


1555


1556


1557


1558


Conclusion

Everything worked without a hitch and no need for any tricky SPI programming or dodgy libraries. Pyserial was all that was required. These are now my wireless modules of choice.

I'm just using the little coiled-wire antenna that came with the module and I'm getting more than enough range to reach the hen house even through a few walls. The modules have a tiny antenna connector, to which an external antenna can be attached for maximum range. The module datasheet claims 1km at maximum power and lowest baud rate. Please check that 433MHz is legal in your area.

Next up for me is using the module's AT commands to balance low power with range and change channel, to avoid interference.

Monday, September 12, 2022

A Better Web Server for Raspberry Pi Pico W

The Pico W is a fantastic little device, but the examples I have found for serving web pages from it were all too low-level for my taste.


I'm big fan of Bottle and Flask as light-weight web frameworks. And was very excited to find the microdot project. This looks very much like Flask and Bottle, using the decorator syntax (@) to mark functions that are to respond to particular web requests. This leads to code as simple as this for a minimal web server.

from microdot import Microdot

app = Microdot()

@app.route('/')
def index(request):
    return 'Hello, world!'

app.run()

Now thats what I call the right level of abstraction. Not a reference to socket in sight!

The great news is that microdot works with the Raspberry Pi Pico. Although it takes care of all the web serving for you, it will not initiate your WiFi connection. Connecting to WiFi has a few subtleties such as showing connection progress and handling failure to connect and re-connection. It's definitely a wheel that doesn't need reinventing every time you want to serve a web page. So, I have put together a tiny module to wrap that process up as well. Its available here: mm_wlan.

Once installed, your entire web server code for your Pico will just become this:

from microdot import Microdot
import mm_wlan

ssid = 'my network name'
password = 'my password'

app = Microdot()  
mm_wlan.connect_to_network(ssid, password)

@app.route('/')
def index(request):
    return 'Hello, from Pico'

app.run(port=80)

Step by Step

1. Install microdot and mm_wlan. This involves copying just two files, microdot.py and mm_wlan.py onto the file system of your Pico W.

You can do this by opening these files in Thonny and, with your Pico W connected to your computer choosing Save copy from the File menu. When prompted select Raspberry Pi Pico as the destination and save the file onto the Pico's file system using its original name.

2. Start a new file in Thonny and paste in the code immediately above. Change ssid and password to match your WiFi network and run it. When you do so, you will see the IP address that the server is running on in the Shell area at the bottom of the Thonny window. 


Now open a web browser on that IP address:

Hurray! Your Pico W is being a web server!

Adding Another Page

Let's now add a second page to the web server called memory that reports how much free memory the Pico W has. The previous page returned plain text rather than HTML. The default response type of microdot is text, so even if we had put HTML tags into the response, it is displayed literally and not be interpreted by the browser as HTML.

For this new page we will need to import the gc (Garbage Collector) package:

import gc

and then add a handler like this:

@app.route('memory')
def index(request):
    response = '<h1>Free Memory={} bytes</hi>'.format(gc.mem_free())
    return response, {'Content-Type': 'text/html'}

Notice that now, as well as returning the response text, we also return the content type.

Run the program and, when you point your browser to the page memory, this is what you should see:


Try refreshing the page a few times and you should see the free memory change.

Here's the whole program:

from microdot import Microdot
import mm_wlan
import gc 
 
sid = 'my network name'
password = 'my passord'

app = Microdot()  
mm_wlan.connect_to_network(ssid, password)  
 
@app.route('/')
def index(request):
    return 'Hello, from Pico' 
 
@app.route('memory')  
 
def index(request):
    response = '<h1>Free Memory={} bytes</hi>'.format(gc.mem_free())
    return response, {'Content-Type': 'text/html'} 
 
app.run(port=80)   

Templates

As your web pages get a bit more complex, then some kind of templating will help you construct the response text. Here's an example of a multi-line example taken from an example project in the MonkMakes Plant Monitor.



from microdot import Microdot
import mm_wlan
from pmon import PlantMonitor

ssid = 'network name'
password = 'password'

html = """
    <!DOCTYPE html>
      <meta http-equiv="refresh" content="1" >
<html>
<head> <title>My Plant</title> </head>
<body>
<h1>Pico W Plant Monitor</h1>
<h2>Water: {water}</h2>
<h2>Temp (C): {temp}</h2>
<h2>Humidity: {humidity}</h2>
</body>
</html>
"""
pm = PlantMonitor()
app = Microdot()
mm_wlan.connect_to_network(ssid, password)

@app.route('/')
def index(request):
    w = pm.get_wetness()
    t = pm.get_temp()
    h = pm.get_humidity()
    response = html.format(water=w, temp=t, humidity=h)
    return response, {'Content-Type': 'text/html'}

app.run(port=80)


The neat trick here is to use the meta tag to automatically refresh the wen page every second. Perhaps a better way to do this would be to have a microdot page that serves the sensor values of JSON that Javascript then handles on the browser, with the home page including the Javascript to do that. But, that's a lot more work.

Summary

The microdot framework will take a lot of the effort out of your Pico W projects where web serving is required. It's a really impressive bit of framework code.

The microdot framework is pretty complete, supporting different request types, request parameters and pretty much anything you could expect from such a compact web server framework.