Hand tools and fabrication machines
The aim of this project is to demonstrate how two LoPy modules can communicate directly to each other via LoRa without using LoRaWAN. One LoPy will send out "Ping" messages which are received by another LoPy module, that will in turn reply with a "Pong" message. When either LoPy receives a message it will light up a string of WS2812 addressable LEDs and play a spooky sound effect.
wave - Used to decode WAV files
chunk - Required by
to decode the WAV header
ws2812alt - Library for controlling the WS2812 LEDs via the LoPy's SPI periferal
Setting up the boards:
Connect the LoPy modules to the expansion boards, ensuring that the LED of the module is facing the same side as the micro-USB connector. Once connected you need to make sure the LoPy firmware is up to date. Instructions on how to update the firmware can be found here.
The above diagram illustrates the complete circuit required for each pumpkin. A speaker is connected to the LoPy via an amplifier to pin
. The LoPy module has a digital to analog converter (DAC) available on pins
which will be used to generate an audio signal. The WS2812 LEDs need to be connected into a single chain as shown in the diagram. For simplicity (and better aesthetics) Adafruit jewel modules are used for the eyes and nose. If you don't carve a nose into your pumpkin you will only require two of these. For the power connections it is recommend to use a terminal block like so:
This step is where you let your creativity shine, the exact design you use is not critical so long as light is able to pass through the pumpkin in places. A guide on carving pumpkins can be found on wikihow.
LoRa - LoRa Communications
Communicating between two LoPy modules using LoRa is quite trivial. This project used the example code found here to achieve this. Please make sure you set the frequency to match the LoRa frequency in your region (
for North America or
For the LEDs I used the wa2812alt library by Gadgetoid. The WS2812 LEDs require very accurate timing, this clever library utilises the SPI peripheral of the ESP32 found on the LoPy module to accurately generate the required signal.
To initialise this library we used the following code:
# 3 jewels for both eyes and nose, 16 LED strip for the mouth num_pixels = 7 * 3 + 16 chain = WS2812(spi_bus=0, led_count=num_pixels)
For this project, rather than changing the colour of the LEDs in a pattern we use the libraries
attribute to fade a fixed pattern in an out. The fixed pattern for the LEDs is setup with the following code:
data = [(0,255,0) for x in range(7*2)] + # Green eyes # a 4 tuple is used below because this jewel is a RGBW one [(0,255,255,0) for x in range(7)] + # Cyan nose [(255,0,0) for x in range(16)] # Red mouth
To fade this pattern in and out we can vary the intensity attribute from 0 to 1. The following code changes the intensity and writes the values out to the LEDs.
for x in [x * 0.1 for x in range(11)]: chain.intensity = x chain.show(data) time.sleep_ms(50)
The LEDs can then be faded back out by using a similar loop with a reversed range. If you want to animate the LEDs with different colours you will need to alter the values within
For the spooky sound effect we will be using WAV files due to their simplicity. WAV files contain a header describing the format and amount of audio samples followed by the raw, uncompressed, audio samples. Due to the lack of compression, it is very simple to parse a WAV file. The first thing we need to do is setup the DAC on pin
#Audio output DAC dac_audio = machine.DAC('P21')
The wave library provided by a micropython example will handle parsing the WAV file for us and return to us the raw audio samples. These samples can then be output to the DAC to generate audio. Note: the DAC present on the ESP32 is not really meant for audio output, and as such you won't be able to reproduce audio sampled at more than 2KHz via micropython.
def play_wav(filepath, dac_pin): f = wave.open(filepath, 'r') max_frames = f.getnframes() sample_rate = f.getframerate() for _ in range(max_frames): sample = f.readframes(1) sample = struct.unpack("h", sample) sample = scale(sample, (-32768, 32767), (0, 1)) dac_pin.write(sample) time.sleep(1.0/sample_rate)
So long as the WAV file is kept small, FTP can be used to upload the WAV file directly to the LoPy. If a long WAV file is used, it is best saved onto a micro SD card which can be plugged into the expansion board.
The values in most WAV files come in the form of signed 16-bit integers, the DAC expects a floating point value in the range 0 to 1. This scaling is quite trivial and can be achieved via the following function:
def scale(val, src, dst): return ((val - src) / (src-src)) * (dst-dst) + dst