Maker Pi Pico with ESP01S Module

It has been a while since my last post, most of which has been spent dealing with other things, as well as waiting for electronics modules to arrive from overseas. A lot of my time has also been spent on getting to grips with the Raspberry Pi Pico, and in particular, the Maker Pi Pico, from Cytron Technologies. This has been an experience with quite a lot of mixed feelings… As Usual, Cytron has done an excellent job with the development board, which, while apparently still in Beta, seems to be rock solid. Most of my frustration came from the “patchy” C/C++ support for the Pi Pico (Yes, I know there are great support, BUT it is not exactly user friendly 🙂 ). That left me with MicroPython, which although I am fluent, are not my goto language…

Lets get back on track though… The Maker Pi Pico has built in support for an ESP01/ESP01S Module. Lets look at the schematic….

Maker Pi Pico Schematic

As we can see, Cytron has provided us with access to Tx (GP17) and Rx(GP16) directly on the Pi Pico. Power (+3.3v) and Ground are connected as well… On this version of the board, no access to IO0,IO2 and the Reset Pin for the ESP01/ESP01S was provided… Maybe this will change in future…?

I have used the standard AT command firmware that comes pre-loaded onto the ESP01/S module. This allows you to send AT commands to the ESP01 Module to control it. It is also possible to setup a transparent WiFi “channel” to communicate between Pico and a remote application , making it possible to control Pico remotely. I have however not prepared and example of that for release yet….

MicroPython Code to communicate with ESP01 from Maker Pi Pico

import uos
from machine import UART, Pin
import utime

"""
ESPRESSIF AT Command Set
https://docs.espressif.com/projects/esp-at/en/latest/AT_Command_Set/
"""

print()
print("Machine: \t" + uos.uname()[4])
print("MicroPython: \t" + uos.uname()[3])

#indicate program started visually
led_onboard = machine.Pin(25, machine.Pin.OUT)
led_onboard.value(0)     # onboard LED OFF/ON for 0.5/1.0 sec
utime.sleep(0.5)
led_onboard.value(1)
utime.sleep(1.0)
led_onboard.value(0)

uart0 = UART(0, rx=Pin(17), tx=Pin(16), baudrate=115200)
# NOTE that we explicitly set the Tx and Rx pins for use with the UART
# If we do not do this, they WILL default to Pin 0 and Pin 1
# Also note that Rx and Tx are swapped, meaning Pico Tx goes to ESP01 Rx 
# and vice versa.
print(uart0)

def sendCMD_waitResp(cmd, uart=uart0, timeout=2000):
    print("CMD: " + cmd)
    uart.write(cmd)
    waitResp(uart, timeout)
    print()
    
def waitResp(uart=uart0, timeout=2000):
    prvMills = utime.ticks_ms()
    resp = b""
    while (utime.ticks_ms()-prvMills)<timeout:
        if uart.any():
            resp = b"".join([resp, uart.read(1)])
    print("resp:")
    try:
        print(resp.decode())
    except UnicodeError:
        print(resp)
    
sendCMD_waitResp('AT\r\n')          #Test AT startup
sendCMD_waitResp('AT+GMR\r\n')      #Check version information
#sendCMD_waitResp('AT+RESTORE\r\n')  #Restore Factory Default Settings
sendCMD_waitResp('AT+CWMODE?\r\n')  #Query the Wi-Fi mode
sendCMD_waitResp('AT+CWMODE=1\r\n') #Set the Wi-Fi mode = Station mode
sendCMD_waitResp('AT+CWMODE?\r\n')  #Query the Wi-Fi mode again
#sendCMD_waitResp('AT+CWLAP\r\n', timeout=10000) #List available APs
sendCMD_waitResp('AT+CWJAP="jean_iot","master123abc"\r\n', timeout=5000) #Connect to AP
utime.sleep(1)
sendCMD_waitResp('AT+CIFSR\r\n')    #Obtain the Local IP Address

You can now extend and adapt this code to suit your purposes…

The ESP01/ESP01S AT Command Datasheet is available for download here

Thank you

Maker Pi Pico – Programming your board

It has been almost a week now since I received my Maker Pi Pico from Cytron Technologies in Malysia. Most of this time has been spent getting to know the RP2040 Microchip, and how to effectively program it. Cytron has done an excellent job being very quick to market with a development board based on the RPi Pico, as well as providing a very good starting foundation to new Pico users ( which I believe is all of us, at least at this stage 🙂 )



It is super easy to put your Maker Pi Pico into Upload Mode. No need to plug and unplug your USB Cable.
– Push and hold the RUN Button ( Located on the Bottom Right, Above the GP20 Push Button) .
– While holding RUN pressed, press the BOOTSEL button on the Pico, and keep it pressed.
– Release RUN and then release BOOTSEL.

You are now in BOOTSEL Mode. You can donload the official Micropython .uf2 file from the link below, or from the Raspberry Pi Website. It is also possible to install Micropython directly from inside the Thonny Python IDE.

You can also find a few examples of code written for the RPi Pico on Cytron’s Github Page

The SDK above contains all the information needed to setup the Thonny IDE to use with your Pico ( Chapter 4 ).

In my next post, I will post some of my own example code for using some additional peripherals.

Thank you

Introducing Maker PI Pico

While everybody in the Maker Community are slowly coming to terms with the release of the RPi Pico (The new development board based on the RP2040 by the Raspberry Pi Foundation) a few days ago, I got my hands on one of the first maker-friendly development boards designed with the Pico in mind. The Maker Pi Pico, made by Cytron Technologies, definitely makes it very easy to get started with the new RPi Pico.

A one-sided SMD development board, packed with useful peripherals, in a 93.98 x 68.58mm form factor, with the following components already onboard:

  • Access to all Raspberry Pi Pico’s pins on two 20 ways pin headers
  • LED indicators on all GPIO pins
  • 3x programmable push button (GP20-22)
  • 1x RGB LED – NeoPixel  (GP28)
  • 1x Piezo buzzer (GP18)
  • 1x 3.5mm stereo audio jack (GP18-19)
  • 1x Micro SD card slot (GP10-15)
  • 1x ESP-01 socket (GP16-17)
  • 6x Grove ports

Over the next few days, I will go into some of this exiting new development board`s features, as well as show you how to program it using Micropython as well as C/C++

UPDATE 5 February 2021

Although the Raspberry Pi Foundation has provided excellent documentation on programming the PICO with C/C++, I will not do any review of that here at this time. The reasons being the following:

1) It seems that PICO was indeed designed as a companion to a Raspberry PI 4B. All the C/C++ tools are geared towards that or Linux. I use Linux in my everyday computing life, but most of you don’t.
2) At the moment, the most reliable way that I can program this board in C/C++ is from the command line.
Support for many IDE’s seems to be available. But it is PAINFUL, to say the least.
VS Source or CLION or ECLIPSE, neither IDE’s that I like or use, due to their clutter and slow performance / unnecessary complexity etc.
3) It is possible to use MS Windows or a MAC, but same rules apply.

Another reason is that you have to enter BOOTSEL mode each time you want to upload or use a debugger, which, if you don’t have a PI, means another PICO. Plugging and unplugging a USB cable that often can not be good for the connector in the long term.

For these reasons, and to keep it simple and easily understandable for everyone, I shall keep to Micropython for the time being.

Please stay in touch for more updates, and feel free to contact me for more information.

Getting Started with the Raspberry Pi Pico — Part 2 of the Pico Series…

Welcome to Part two of my RPi Pico Series. You can buy yours from Cytron Technologies.
In this post, we will look at some more of the features of the board, as well as how to get started using this development board. I will focus on MicroPython in this post, and cover C/C++ in the next post.

But before we do that, lets run over some of the specifications of the board first…
The Python Stuff will be at the end of this post…

Board Specifications

Raspberry Pi Pico is a low-cost, high-performance microcontroller board with flexible digital interfaces. Key features include:

  • RP2040 microcontroller chip designed by Raspberry Pi in the United Kingdom
  • Dual-core Arm Cortex M0+ processor, flexible clock running up to 133 MHz
  • 264KB of SRAM, and 2MB of on-board Flash memory
  • Castellated module allows soldering direct to carrier boards
  • USB 1.1 with device and host support
  • Low-power sleep and dormant modes
  • Drag-and-drop programming using mass storage over USB
  • 26 × multi-function GPIO pins
  • 2 × SPI, 2 × I2C, 2 × UART, 3 × 12-bit ADC, 16 × controllable PWM channels
  • Accurate clock and timer on-chip
  • Temperature sensor
  • Accelerated floating-point libraries on-chip
  • 8 × Programmable I/O (PIO) state machines for custom peripheral support

Utilities

What is on your Pico?

If you have forgotten what has been programmed into your Raspberry Pi Pico, and the program was built using our Pico C/C++ SDK, it will usually have a name and other useful information embedded into the binary. You can use the Picotool command line utility to find out these details. Full instructions on how to use Picotool to do this are available in the ‘getting started‘ documentation.

Pico Github Repo

Debugging using another Raspberry Pi Pico

It is possible to use one Raspberry Pi Pico to debug another Pico. This is possible via picoprobe, an application that allows a Pico to act as a USB → SWD and UART converter. This makes it easy to use a Pico on non-Raspberry Pi platforms such as Windows, Mac, and Linux computers where you don’t have GPIOs to connect directly to your Pico. Full instructions on how to use Picoprobe to do this are available in the ‘getting started‘ documentation.

PicoProbe Github Repo

Resetting Flash memory

Pico’s BOOTSEL mode lives in read-only memory inside the RP2040 chip, and can’t be overwritten accidentally. No matter what, if you hold down the BOOTSEL button when you plug in your Pico, it will appear as a drive onto which you can drag a new UF2 file. There is no way to brick the board through software. However, there are some circumstances where you might want to make sure your Flash memory is empty. You can do this by dragging and dropping a special UF2 binary onto your Pico when it is in mass storage mode.

The Code for the flash eraser is available below


/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Iden
/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

// Obliterate the contents of flash. This is a silly thing to do if you are
// trying to run this program from flash, so you should really load and run
// directly from SRAM. You can enable RAM-only builds for all targets by doing:
//
// cmake -DPICO_NO_FLASH=1 ..
//
// in your build directory. We've also forced no-flash builds for this app in
// particular by adding:
//
// pico_set_binary_type(flash_nuke no_flash)
//
// To the CMakeLists.txt app for this file. Just to be sure, we can check the
// define:
#if !PICO_NO_FLASH
#error "This example must be built to run from SRAM!"
#endif

#include "pico/stdlib.h"
#include "hardware/flash.h"
#include "pico/bootrom.h"

int main() {
    flash_range_erase(0, PICO_FLASH_SIZE_BYTES);
    // Leave an eyecatcher pattern in the first page of flash so picotool can
    // more easily check the size:
    static const uint8_t eyecatcher[FLASH_PAGE_SIZE] = "NUKE";
    flash_range_program(0, eyecatcher, FLASH_PAGE_SIZE);

    // Flash LED for success
    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    for (int i = 0; i < 3; ++i) {
        gpio_put(PICO_DEFAULT_LED_PIN, 1);
        sleep_ms(100);
        gpio_put(PICO_DEFAULT_LED_PIN, 0);
        sleep_ms(100);
    }

    // Pop back up as an MSD drive
    reset_usb_boot(0, 0);
}tifier: BSD-3-Clause
 */

// Obliterate the contents of flash. This is a silly thing to do if you are
// trying to run this program from flash, so you should really load and run
// directly from SRAM. You can enable RAM-only builds for all targets by doing:
//
// cmake -DPICO_NO_FLASH=1 ..
//
// in your build directory. We've also forced no-flash builds for this app in
// particular by adding:
//
// pico_set_binary_type(flash_nuke no_flash)
//
// To the CMakeLists.txt app for this file. Just to be sure, we can check the
// define:
#if !PICO_NO_FLASH
#error "This example must be built to run from SRAM!"
#endif

#include "pico/stdlib.h"
#include "hardware/flash.h"
#include "pico/bootrom.h"

int main() {
    flash_range_erase(0, PICO_FLASH_SIZE_BYTES);
    // Leave an eyecatcher pattern in the first page of flash so picotool can
    // more easily check the size:
    static const uint8_t eyecatcher[FLASH_PAGE_SIZE] = "NUKE";
    flash_range_program(0, eyecatcher, FLASH_PAGE_SIZE);

    // Flash LED for success
    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    for (int i = 0; i < 3; ++i) {
        gpio_put(PICO_DEFAULT_LED_PIN, 1);
        sleep_ms(100);
        gpio_put(PICO_DEFAULT_LED_PIN, 0);
        sleep_ms(100);
    }

    // Pop back up as an MSD drive
    reset_usb_boot(0, 0);
}

Getting Started with MicroPython on the RPi Pico

Drag and drop MicroPython

You can program your Pico by connecting it to a computer via USB, then dragging and dropping a file onto it, so we’ve put together a downloadable UF2 file to let you install MicroPython more easily. Following the procedure below, you can install MicroPython onto the Pico in a few seconds…

1. Download and unzip the UF2 file below into a folder on your computer.

2. Hold down the Bootsel button on the Pico, and connect it to a USB cable that was already connected to your computer. ( This means, connect ons side of the usb cable to the computer, but dont connect the Pico yet. then hold down BOOTSEL, and connect the cable to the pico)

3. Now, release the BootSEL button

4. After a few Seconds, you will have a new USB storage device on your computer, called RPI-RP2

5. Drag the UF2 file into this USB Storage device. The Pico will reboot… You have now installed MicroPython on your RPi Pico

Accessing the MicroPython REPL

You can now access REPL from a serial terminal, or a MicroPython IDE , like Thonny…
I will show you how to do it from the Linux Terminal below.

The Pico will show up as a USB device called ttyACM0
you can find it by issuing the ls /dev/tty* command

Start minicom or your preferred serial terminal emulator

Press enter a few times, and you should get a REPL prompt

You can now test MicroPython on your Pico, by typing the following commands:

The complete MicroPython SDK for the RPi Pico is available for download at the link below…

In the next part of this series, we will look at using the Thonny IDE, as well as C/C++ to program the Pico

Introducing the Raspberry Pi Pico

It is not often that we get the opportunity to be one of the first people to get our hands onto a new product, So when my friends at Cytron Technologies asked me if I would like to do a review on a new Raspberry Pi product last week, I was definitely interested. Details were few, as the product was still under an NDA, but at last, I got the datasheets and some details on Tuesday, enough to start writing about the new product before the big Launch on Thursday the 21st of January 2021…

So, what am I trying to say? Well, It seems that the Raspberry Pi Foundation has released a new product, and from first impressions, it seems to be a game-changer… Lets not get confused. I am not speaking about a full size Raspberry Pi Board, or the compute module… No, The Pi Foundation has released an RP2040 Microprocessor based development board, in the same form factor as an Arduino Nano.

Raspberry Pi Pico Microcontroller Board

This will be an introduction post, and when I receive the device to play with, which will be soon, I will start with a short series on its features and capabilities… For now, lets look at some of the specifications

Front and Back view of the Raspberry Pi Pico

Features:

Raspberry Pi Pico has been designed to be a low cost yet flexible development platform for RP2040, with the following
key features:
• RP2040 microcontroller with 2MByte Flash
• Micro-USB B port for power and data (and for reprogramming the Flash)
• 40 pin 21×51 ‘DIP’ style 1mm thick PCB with 0.1″ through-hole pins also with edge castellations
â—¦ Exposes 26 multi-function 3.3V General Purpose I/O (GPIO)
â—¦ 23 GPIO are digital-only and 3 are ADC capable
â—¦ Can be surface mounted as a module
• 3-pin ARM Serial Wire Debug (SWD) port
• Simple yet highly flexible power supply architecture
â—¦ Various options for easily powering the unit from micro-USB, external supplies or batteries
• High quality, low cost, high availability
• Comprehensive SDK, software examples and documentation
RP2040 key features: (Datasheet available for download at the bottom of this post)
• Dual-core cortex M0+ at up to 133MHz
â—¦ On-chip PLL allows variable core frequency
• 264K multi-bank high performance SRAM
• External Quad-SPI Flash with eXecute In Place (XIP)
• High performance full-crosspoint bus architecture
• On-board USB1.1 (device or host)
• 30 multi-function General Purpose IO (4 can be used for ADC)
â—¦ 1.8-3.3V IO Voltage (NOTE Pico IO voltage is fixed at 3.3V)
• 12-bit 500ksps Analogue to Digital Converter (ADC)
• Various digital peripherals
â—¦ 2x UART, 2x I2C, 2x SPI, up to 16 PWM channels
â—¦ 1x Timer with 4 alarms, 1x Real Time Counter
• Dual Programmable IO (PIO) peripherals
â—¦ Flexible, user-programmable high-speed IO
â—¦ Can emulate interfaces such as SD Card and VGA

Pico provides minimal (yet flexible) external circuitry to support the RP2040 chip (Flash, crystal, power supplies and
decoupling and USB connector). The majority of the RP2040 microcontroller pins are brought to the user IO pins on the left and right edge of the board. Four RP2040 IO are used for internal functions – driving an LED, on-board Switched Mode Power Supply (SMPS) power control and sensing the system voltages.
Pico has been designed to use either soldered 0.1″ pin-headers (it is one 0.1″ pitch wider than a standard 40-pin DIP package) or can be used as a surface mountable ‘module’, as the user IO pins are also castellated. There are SMT pads underneath the USB connector and BOOTSEL button, which allow these signals to be accessed if used as a reflow-soldered SMT module.

The Pico uses an on-board buck-boost SMPS which is able to generate the required 3.3 volts (to power RP2040 and external circuitry) from a wide range of input voltages (~1.8 to 5.5V). This allows significant flexibility in powering the unit from various sources such as a single Lithium-Ion cell, or 3 AA cells in series. Battery chargers can also be very easily integrated with the Pico powerchain.
Reprogramming the Pico Flash can be done using USB (simply drag and drop a file onto the Pico which appears as a mass storage device) or via the Serial Wire Debug (SWD) port. The SWD port can also be used to interactively debug code running on the RP2040.

Mechanical Specifications

The Raspberry Pi Pico is a single sided 51x21mm 1mm thick PCB with a micro-USB port overhanging the top edge and dual castellated/through-hole pins around the remaining edges. Pico is designed to be usable as a surface mount module as well as being in Dual Inline Package (DIP) type format, with the 40 main user pins on a 2.54mm (0.1″) pitch grid with 1mm holes and hence compatible with veroboard and breadboard. Pico also has 4x 2.1mm (+/- 0.05mm) drilled mounting holes to provide for mechanical fixing, see Figure 3.

Mechanical specifications for the Raspberry Pi Pico

Conclusion

I hope that this is enough details to get all of you interested and eager for more details…
In the next part of this series, I will focus on getting started with this new board, as well as do the official unboxing…
Please stay tuned for more details…

Multiple LoRa Device Communication

In this part of my LoRa Series ( Part 3 ) I will look at some basic code for the Heltec LoRa 32 V2 Module. This code will in particular be focused on Multiple device communication. It can easily adapted from the stock example (as provided below) to implement a custom addressing scheme.

LoRa Multiple Communications No Interrupt

#include "heltec.h"

#define BAND    433E6  //you can set band here directly,e.g. //868E6,915E6


String outgoing;              // outgoing message

byte localAddress = 0xBB;     // address of this device
byte destination = 0xFD;      // destination to send to

byte msgCount = 0;            // count of outgoing messages
long lastSendTime = 0;        // last send time
int interval = 2000;          // interval between sends

void setup()
{
   //WIFI Kit series V1 not support Vext control
  Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.LoRa Enable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/);

  Serial.println("Heltec.LoRa Duplex");

 
}

void loop()
{
  if (millis() - lastSendTime > interval)
  {
    String message = "Hello there!";   // send a message
    sendMessage(message);
    Serial.println("Sending " + message);
    lastSendTime = millis();            // timestamp the message
    interval = random(2000) + 1000;    // 2-3 seconds
  }

  // parse for a packet, and call onReceive with the result:
  onReceive(LoRa.parsePacket());
}

void sendMessage(String outgoing)
{
  LoRa.beginPacket();                   // start packet
  LoRa.write(destination);              // add destination address
  LoRa.write(localAddress);             // add sender address
  LoRa.write(msgCount);                 // add message ID
  LoRa.write(outgoing.length());        // add payload length
  LoRa.print(outgoing);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
}

void onReceive(int packetSize)
{
  if (packetSize == 0) return;          // if there's no packet, return

  // read packet header bytes:
  int recipient = LoRa.read();          // recipient address
  byte sender = LoRa.read();            // sender address
  byte incomingMsgId = LoRa.read();     // incoming msg ID
  byte incomingLength = LoRa.read();    // incoming msg length

  String incoming = "";

  while (LoRa.available())
  {
    incoming += (char)LoRa.read();
  }

  if (incomingLength != incoming.length())
  {   // check length for error
    Serial.println("error: message length does not match length");
    return;                             // skip rest of function
  }

  // if the recipient isn't this device or broadcast,
  if (recipient != localAddress && recipient != 0xFF) {
    Serial.println("This message is not for me.");
    return;                             // skip rest of function
  }

  // if message is for this device, or broadcast, print details:
  Serial.println("Received from: 0x" + String(sender, HEX));
  Serial.println("Sent to: 0x" + String(recipient, HEX));
  Serial.println("Message ID: " + String(incomingMsgId));
  Serial.println("Message length: " + String(incomingLength));
  Serial.println("Message: " + incoming);
  Serial.println("RSSI: " + String(LoRa.packetRssi()));
  Serial.println();
}

LoRa Multiple communication, Interrupt

#include "heltec.h"

#define BAND    433E6  //you can set band here directly,e.g. 868E6,915E6

  
byte localAddress = 0xBB;     // address of this device
byte destination = 0xFF;      // destination to send to

String outgoing;              // outgoing message
byte msgCount = 0;            // count of outgoing messages
long lastSendTime = 0;        // last send time
int interval = 2000;          // interval between sends

void setup()
{
   //WIFI Kit series V1 not support Vext control
  Heltec.begin(true /*DisplayEnable Enable*/, true /*Heltec.LoRa Disable*/, true /*Serial Enable*/, true /*PABOOST Enable*/, BAND /*long BAND*/);

  LoRa.onReceive(onReceive);
  LoRa.receive();
  Serial.println("Heltec.LoRa init succeeded.");
}

void loop()
{
  if (millis() - lastSendTime > interval)
  {
    String message = "Hello World!";   // send a message
    sendMessage(message);
    Serial.println("Sending " + message);
    lastSendTime = millis();            // timestamp the message
    interval = random(2000) + 1000;     // 2-3 seconds
    LoRa.receive();                     // go back into receive mode
  }
}

void sendMessage(String outgoing)
{
  LoRa.beginPacket();                   // start packet
  LoRa.write(destination);              // add destination address
  LoRa.write(localAddress);             // add sender address
  LoRa.write(msgCount);                 // add message ID
  LoRa.write(outgoing.length());        // add payload length
  LoRa.print(outgoing);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
}

void onReceive(int packetSize)
{
  if (packetSize == 0) return;          // if there's no packet, return

  // read packet header bytes:
  int recipient = LoRa.read();          // recipient address
  byte sender = LoRa.read();            // sender address
  byte incomingMsgId = LoRa.read();     // incoming msg ID
  byte incomingLength = LoRa.read();    // incoming msg length

  String incoming = "";                 // payload of packet

  while (LoRa.available())             // can't use readString() in callback
  {
    incoming += (char)LoRa.read();      // add bytes one by one
  }

  if (incomingLength != incoming.length())   // check length for error
  {
    Serial.println("error: message length does not match length");
    return;                             // skip rest of function
  }

  // if the recipient isn't this device or broadcast,
  if (recipient != localAddress && recipient != 0xFF)
  {
    Serial.println("This message is not for me.");
    return;                             // skip rest of function
  }

  // if message is for this device, or broadcast, print details:
  Serial.println("Received from: 0x" + String(sender, HEX));
  Serial.println("Sent to: 0x" + String(recipient, HEX));
  Serial.println("Message ID: " + String(incomingMsgId));
  Serial.println("Message length: " + String(incomingLength));
  Serial.println("Message: " + incoming);
  Serial.println("RSSI: " + String(LoRa.packetRssi()));
  Serial.println();
}

LoRa – Part 2

So many people asked me which Lora Module I use for my projects. In this part of the series, I will show you, as well as shed some light on another module, that although seemingly cheap, is, unfortunately, according to me, a complete waste of time and money.

Heltec LoRa 32 v 2 – The good stuff ( according to me at least)

Technical Specs
Electrical Specifications
Pinout

Installation in Arduino IDE:

Installation of the libraries into the Arduino IDE is quite easy, just follow the link to heltec…

The Bad ( according to me )

The following module, is, according to me, an absolute waste of time and money. Documentation is impossible to find, and that that you do find, as often incorrect. The pin-outs are wrong, with no definite standard.

I am talking about the TTGO Lora V1 or V2 or what ever ??? can seem to even find that answer reliably. I was initially attracted to this module, as it was allegedly compatible with the heltec version, and did not have the oled screen, which, to be honest, is not always needed in every project. It was also about 25% cheaper, and could be sourced locally, without enriching the greedy shipping companies 😉 (I just have to rant about this, as 25USD to ship 100g worth of stuff is a ripoff. Either that or 60 to 90 days of guess-if-it-will-arrive mail is not on ( and even that is 10 USD!)

So, having high hopes, I ordered one of these boards, hoping to use it together with my heltec boards… It arrived, and that was well the top came of.. I could immediately see that the quality of the PCB was quite bad. Documentation was missing, and even the supplier sent me to a heltec pinout, which, after a quick test were definitely not correct…

Google turned up mixed results, and eventually I found a sort of accurate pinout …

Alleged pinout for TTGO LoRa device

This pinout also turned out to be only about 50% correct, and after manually trying to map out the pins, I was sort of confident enough to test it further…

Further problems arose, LoRa does not work, I2C does not work, SPI does not work shall I continue…? 🙂 It now seems clear that the board that I bought was a clone of a clone, and a very bad one at that …
I will post a picture of the actual board below, in the interest of education, to inform others not to get duped as well. Likewise, If I am the mistaken party, and you have had success with this board, please give me a message/yell and lets share some knowledge

The Front (Top Side) of the Module

Front (Top) of the module
Back (Bottom) of the module

I hope that you found this useful and that I will see you for part 3 of the series, where I will get into the actual coding.

What is LoRa?

Introduction

When designing IoT solutions, we all encounter the problem of connecting our device(s) to each other, either directly, or through the internet. In Urban areas, it is quite easy to use WiFi or even GSM to achieve this, but these solutions often come with additional costs in the form of subscriptions. Although it is possible to run your own WiFi network free of charge, you will soon run into issues with the range…

Enter LoRa (short for Long Range) Radio communication. LoRa is a radio technology derived from chirp spread spectrum technology. It uses an ISM band, meaning it is unregulated in most countries, if you use the correct frequency for your country, that is.

It is also extremely low power, making it ideal for use with battery-powered devices.
The technology is available in Node-to-Node, as well as Node-to -Gateway modes.

In this series, I will show you how to use a few of the existing LoRa Modules available on the market.

Ai-Tinker Ra-02 (Sx1278)

Ra-02 Lora Module, with spring antenna, by Ai-Tinker

This Module is conveniently broken-out onto a breakout board. It is sort of bread-board friendly (depending on the size of your bread-board) and is nicely labelled. It is also extremely cheap ( around $USD5 each, depending on where you buy from).

Caveats

There are quite a few important things that you should know about these modules before you start using them.

Disclaimer: The caveats listed below are by no means complete, or even valid. They are the result of experimentation by myself, with the intent to destroy a few modules, to see how hardy they are. Also take into mind, that living in SE Asia, it is quite common to buy something from a shop, where the seller has no or only a very limited idea of what he or she is selling, and are thus usually quite unable to provide any technical support.

To summarise: USE YOUR HEAD. If I did leave out something, it is quite possible that I forgot, or decided not to include it on purpose. This is a general guide, and you should ideally do your own research as well. That is the best way to learn.



1) Always connect an Antenna. This may seem like a logical one, but it is extremely important. The module is capable of quite a lot of transmission power, and operating it without an antenna will quickly damage the module, permanently.

2) ONLY use 3.3v, even on the control lines (the module uses SPI). This is quite important, as it is not very clearly stated by the suppliers, and will result in very short-lived component operation 😉 If you absolutely have to use 5v, use a level converter. (There are examples available on the internet, where they use this chip directly from an Arduino Uno. I can confirm that that approach does work, BUT, not for very long. I have purposely sacrificed a pair of transceiver modules so that you don’t have to. You can also adjust the SPI frequency, in the event that your level converter is not capable of running at a high SPI frequency.

3)Make sure that you connect ALL the ground pins on the device. This is another area that is not fully explained by the user manual and does “unexplainably” result in damaged modules.

4) Use short, good quality cables, and if possible, keep the module off the breadboard.
While testing the modules, I found that the usual DuPont wires, as everyone should know by now, are quite unreliable. Combine that with a bread-board that has seen its share of use,
and it is a definite recipe for headache 🙂

5) LoRa Antennas are polarised, make sure you have your antennas in the same orientation.
Although this will not prevent it from working over short distances, it makes sense to just do it correctly. Good RF practices never hurt anybody 🙂

Connecting to Arduino

A Note on Power:
It is important to power this module from a decent dedicated 3.3v power-supply.
The Arduino Uno does sometimes have a 3.3v regulator on-board. From my tests, it is however not always up to the task, as the module may spike up to 120mA when transmitting. It is thus also recommended to have a nice fat capacitor across the power lines (decoupling cap) to soak up any spikes.

As mentioned above, a level converter is mandatory for a 5v Arduino. You may do without it if you use a 3.3v Arduino, but once again, your mileage will vary 🙂

Both the transmitter and receiver uses the same connections, which are listed below:

LoRa SX1278 ModuleArduino Board
3.3V
GndGnd
En/NssD10
G0/DIO0D2
SCKD13
MISOD12
MOSID11
RSTD9
Connections to the Arduino from a LoRa RA-02 Module

Remember that you NEED a Level converter between the LoRa Module and the Arduino.

Software Library

The software library that we will use in our example is the excellent library from Sandeep Mistry. We will just include this into the Arduino IDE, and then use a slightly modified version of the examples for our experiment. It is also important to note that we will use Node-to Node communication, NOT LoRaWan. This means that all your communications will essentially be unencrypted, and not addressed. This does however allow you the flexibility to design and implement your own addressing scheme.

LORA code for Transmitting Side

#include <SPI.h>
#include <LoRa.h>

int counter = 0;

void setup() {
  Serial.begin(115200);
  while (!Serial);

  Serial.println("LoRa Sender");

  if (!LoRa.begin(433E6)) { // Set the frequency to that of your  //module. Mine uses 433Mhz, thus I have set it to 433E6
    Serial.println("Starting LoRa failed!");
    while (1);
  }

  LoRa.setTxPower(20);
  
}

void loop() {
  Serial.print("Sending packet: ");
  Serial.println(counter);

  // send packet
  LoRa.beginPacket();
  LoRa.print("hello ");
  LoRa.print(counter);
  LoRa.endPacket();

  counter++;

  delay(5000);
}
LORA code for Receiver Side

#include <SPI.h>
#include <LoRa.h>

void setup() {
  Serial.begin(115200);
  while (!Serial);

  Serial.println("LoRa Receiver");

  if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
}

void loop() {
  // try to parse packet
  int packetSize = LoRa.parsePacket();
  if (packetSize) {
    // received a packet
    Serial.print("Received packet '");

    // read packet
    while (LoRa.available()) {
      Serial.print((char)LoRa.read());
    }

    // print RSSI of packet
    Serial.print("' with RSSI ");
    Serial.println(LoRa.packetRssi());
  }
}

Where to from here?

If all went well, you will see packets being received in the serial monitor of the Arduino IDE, connected to the receiver module. You will also see that the data from this example is sent as a string… It is however also possible to send binary data, by using the LoRa.write() function.

In the next part of this series, I will show you how to use LoRa with the ESP32/ESP8266,
as well as a working example with binary data transmission and an addressing scheme in part 3.

Thank you

MCP23017 with Adafruit Library

In a previous post, I have shown you how to use the MCP23017 16 Port I2C I/O Port extender with the standard Wire library, as supplied with the Arduino IDE. In this post,
I will have a quick look at using Adafruit’s library for this IC. I believe that this library brings a lot of ease-of-use to the part, making it possible to obscure some of the complexity of I2C.

I do however prefer to use the native Wire library myself, as it is slightly faster.

You can download the Adafruit MCP23017 Library from here..

Pin Addressing

When using single pin operations such as pinMode(pinId, dir) or digitalRead(pinId) or digitalWrite(pinId, val) then the pins are addressed using the ID’s below. For example, for set the mode of GPB0 then use pinMode(8, …).

Physical Pin #Pin NamePin ID
21GPA00
22GPA11
23GPA22
24GPA33
25GPA44
26GPA55
27GPA66
28GPA77
1GPB08
2GPB19
3GPB210
4GPB311
5GPB412
6GPB513
7GPB614
8GPB715

Some examples, directly from the library, all code belongs to Adafruit, and was not written by me.

1. A Button Example

#include <Wire.h>
#include "Adafruit_MCP23017.h"

// Basic pin reading and pullup test for the MCP23017 I/O expander
// public domain!

// Connect pin #12 of the expander to Analog 5 (i2c clock)
// Connect pin #13 of the expander to Analog 4 (i2c data)
// Connect pins #15, 16 and 17 of the expander to ground (address selection)
// Connect pin #9 of the expander to 5V (power)
// Connect pin #10 of the expander to ground (common ground)
// Connect pin #18 through a ~10kohm resistor to 5V (reset pin, active low)

// Input #0 is on pin 21 so connect a button or switch from there to ground

Adafruit_MCP23017 mcp;
  
void setup() {  
  mcp.begin();      // use default address 0

  mcp.pinMode(0, INPUT);
  mcp.pullUp(0, HIGH);  // turn on a 100K pullup internally

  pinMode(13, OUTPUT);  // use the p13 LED as debugging
}



void loop() {
  // The LED will 'echo' the button
  digitalWrite(13, mcp.digitalRead(0));
}

2. An Interrupt Example

// Install the LowPower library for optional sleeping support.
// See loop() function comments for details on usage.
//#include <LowPower.h>

#include <Wire.h>
#include <Adafruit_MCP23017.h>

Adafruit_MCP23017 mcp;

byte ledPin=13;

// Interrupts from the MCP will be handled by this PIN
byte arduinoIntPin=3;

// ... and this interrupt vector
byte arduinoInterrupt=1;

volatile boolean awakenByInterrupt = false;

// Two pins at the MCP (Ports A/B where some buttons have been setup.)
// Buttons connect the pin to grond, and pins are pulled up.
byte mcpPinA=7;
byte mcpPinB=15;

void setup(){

  Serial.begin(9600);
  Serial.println("MCP23007 Interrupt Test");

  pinMode(arduinoIntPin,INPUT);

  mcp.begin();      // use default address 0
  
  // We mirror INTA and INTB, so that only one line is required between MCP and Arduino for int reporting
  // The INTA/B will not be Floating 
  // INTs will be signaled with a LOW
  mcp.setupInterrupts(true,false,LOW);

  // configuration for a button on port A
  // interrupt will triger when the pin is taken to ground by a pushbutton
  mcp.pinMode(mcpPinA, INPUT);
  mcp.pullUp(mcpPinA, HIGH);  // turn on a 100K pullup internally
  mcp.setupInterruptPin(mcpPinA,FALLING); 

  // similar, but on port B.
  mcp.pinMode(mcpPinB, INPUT);
  mcp.pullUp(mcpPinB, HIGH);  // turn on a 100K pullup internall
  mcp.setupInterruptPin(mcpPinB,FALLING);

  // We will setup a pin for flashing from the int routine
  pinMode(ledPin, OUTPUT);  // use the p13 LED as debugging
  
}

// The int handler will just signal that the int has happen
// we will do the work from the main loop.
void intCallBack(){
  awakenByInterrupt=true;
}

void handleInterrupt(){
  
  // Get more information from the MCP from the INT
  uint8_t pin=mcp.getLastInterruptPin();
  uint8_t val=mcp.getLastInterruptPinValue();
  
  // We will flash the led 1 or 2 times depending on the PIN that triggered the Interrupt
  // 3 and 4 flases are supposed to be impossible conditions... just for debugging.
  uint8_t flashes=4; 
  if(pin==mcpPinA) flashes=1;
  if(pin==mcpPinB) flashes=2;
  if(val!=LOW) flashes=3;

  // simulate some output associated to this
  for(int i=0;i<flashes;i++){  
    delay(100);
    digitalWrite(ledPin,HIGH);
    delay(100);
    digitalWrite(ledPin,LOW);
  }

  // we have to wait for the interrupt condition to finish
  // otherwise we might go to sleep with an ongoing condition and never wake up again.
  // as, an action is required to clear the INT flag, and allow it to trigger again.
  // see datasheet for datails.
  while( ! (mcp.digitalRead(mcpPinB) && mcp.digitalRead(mcpPinA) ));
  // and clean queued INT signal
  cleanInterrupts();
}

// handy for interrupts triggered by buttons
// normally signal a few due to bouncing issues
void cleanInterrupts(){
  EIFR=0x01;
  awakenByInterrupt=false;
}  

/**
 * main routine: sleep the arduino, and wake up on Interrups.
 * the LowPower library, or similar is required for sleeping, but sleep is simulated here.
 * It is actually posible to get the MCP to draw only 1uA while in standby as the datasheet claims,
 * however there is no stadndby mode. Its all down to seting up each pin in a way that current does not flow.
 * and you can wait for interrupts while waiting.
 */
void loop(){
  
  // enable interrupts before going to sleep/wait
  // And we setup a callback for the arduino INT handler.
  attachInterrupt(arduinoInterrupt,intCallBack,FALLING);
  
  // Simulate a deep sleep
  while(!awakenByInterrupt);
  // Or sleep the arduino, this lib is great, if you have it.
  //LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_OFF);
  
  // disable interrupts while handling them.
  detachInterrupt(arduinoInterrupt);
  
  if(awakenByInterrupt) handleInterrupt();
}


I hope that this shows you another way of using this versatile IC, 
In a future post, I will show you how to do interrupts, using the native Wire library, as well as point out a few things about why interrrupts sometimes does not seem to be working, as well as a workaround for that.

A Business Card with a Purpose

I recently got some inspiration from the JLCPCB User Group on Facebook. Catherine showed off her PCB style business card, and I just had to had one myself. As it is also
time to get some new cards of my own, I decided to do a PCB version, that can be handed out to very special customers, but with a twist… I added a functional Arduino Nano style
circuit to the business card, complete with microUSB port etc.

New Business Card – Front Side

My plan is to have some of these manufactured at Jlcpcb together with my next order, to save on shipping 😉 I am planning to have it done with a black soldermask, as well as real gold surface treatment.

Back of the Business card

I will leave the Gold PCB without components, or maybe have a few assembled, have not decided yet 🙂

You can get access to the entire project, in case you are also inspired to do your own
on GitHub here: https://github.com/makeriot2020/BusinessCard-Nano
or on the EasyEDA Software here: https://easyeda.com/jean.redelinghuys/bussinescard

I hope someone is inspired enough to try their own, or if you are so inclined, order some from me. If I get enough of a response, I will a standard PCB run, but with lead-free HASL 🙂