empowering creative people

Pycom GPy Getting Started

The main thing I want you to get out of this project is this: upon completion you will have cellular data connected to your Internet of Things device! LoRa, Sigfox, Bluetooth and WiFi can't compete with cellular data for coverage, speed and data allowance. Many projects only become possible when using cellular data. Need to send pictures? Sure. Over the air updates? Yes. Real time streaming data? That too. Just imagine the possibilities!

A disclaimer up front. The use of mobile (cellular) phone networks in IoT devices requires several things to work correctly. This article covers use of the GPy 1.0 from Pycom and so requires you have a SIM card from a carrier supporting either Cat-M1 or NB1. The GPy has separate firmware files for Cat-M1 and NB1 and you must install the firmware for the appropriate standard first (more later). You'll need to test your SIM card in a phone first to ensure it works. You'll also likely need to know some carrier settings which you'll use to configure the LTE device each time it is used. These may be published by your carrier but are unlikely to be Pycom-specific.

I spent time over a few weeks getting my head around all this. Pycom's sample code failed hard, but that's not their fault; it's simply not possible to put up working code for every country/carrier/standard in use today. I tell you this up front so that you're prepared for disappointment. It took me weeks to get an Internet connection on my GPy. Now that I've learned a lot and worked through many problems my GPy connects first time, every time. Be prepared to put some time into making this work on your selected mobile network. That said, if you're in Australia and using Telstra Cat-M1 this code should drop in and work first time!


To replicate this project, you'll need these Pycom parts:

Probably the most obvious thing I should have put in the Overview video was the location and orientation of the SIM card socket. On unboxing the GPy you can see the SIM socket underneath requires a nano SIM. The easiest SIM card I had to hand was the one from my ageing iPhone 6. I shut down the phone, withdrew the SIM and inserted it into the GPy using the correct orientation shown on the diagram.

GPy nano-SIM socket is as per the FiPyThe GPy and FiPy are different, but the SIM socket has the same location and orientation.


  1. Insert the nano SIM into the back of the GPy. The diagram above gives the orientation.
  2. Mount the GPy on the Expansion Board 3.0 remembering to put the RGB LED at the same end as the USB socket.
  3. Connect the cellular antenna at the "left side" next to the reset button
  4. Connect the USB cable to your device and Windows computer

Windows will install the driver automatically. We'll use Atom as our software development environment with the pymakr plugin loaded. If you don't have these set up already please look at Internet of Things with Pycom and Adafruit IO - From Zero to Hero for detailed instructions.

It would help to understand a little of the internals of the GPy. We can really think of it as three computers in one. The main one is the ESP32 microcontroller, it's what runs the MicroPython programs we create. The ESP32 has a built in "real time clock" (RTC). Whenever we need the date and time, we can get it from the RTC. Only one catch: the GPy doesn't have an internal battery, so every time we unplug the USB cable, the RTC "forgets". But while we keep power connected the RTC keeps track of the date and time, even if we reset the GPy. This means if we want to force the RTC to lose the date and time, we must unplug the GPy.

The other computer inside is the LTE modem. This is the guy who's using the SIM card and the antenna to connect to the cellular network. Like the RTC, the LTE device is independent. If we tell it to connect to the Internet, it can stay connected even if we reset the GPy. Again, if we need to force the LTE device to drop its internet connection our only option is to unplug the USB. Of course, the ideal is that we build our MicroPython code in such a way that it works regardless of when the reset button is pushed or the USB cable is disconnected and reconnected.


Are you ready for this? We have three lots of firmware to update. To guarantee success we need to do them in the correct order. We'll do:

  1. Expansion Board 3.0, then
  2. GPy, then
  3. LTE modem on the GPy (Cat-M1 or NB1, not both)

Firmware for Expansion Board 3.0

As updating any of the expansion boards is a bit finnicky I've documented that as a separate article: Pycom Pysense & Pytrack: How to Update Firmware. This documentation now also applies to Pyscan and Expansion Board 3.0, but Expansion Board 3.0 is the only one with two buttons. Please use the S1 button, not the Safe Boot button. Also, you do this without mounting the GPy on top.

Having followed the above procedure and substituting the correct device firmware update file (expansion3_0.0.9.dfu) I got a successful update.

Successful firmware update for the Expansion Board 3.0Successful firmware update for the Expansion Board 3.0

Firmware for GPy

Unplugging the USB cable, I then replaced the GPy on the expansion board. The update on the GPy is a much simpler process. I started the Pycom Firmware Update app I already had installed on my system and found I hadn't installed the latest version. I downloaded 1.14.2 before starting the firmware upgrade process again. For more info, see 1.2.2 Updating Firmware in the Pycom documentation.

The Setup Instructions dialog is a little out of date. The instructions for Pytrack/Pysense should include Pyscan and Expansion Board 3.0. All four boards have their own built-in microcontrollers and do not need a wire jumper to get them into firmware upgrade mode. Play along as if you have a Pytrack or Pysense.

Firmware update successful on the Pycom GPyFirmware update successful on the Pycom GPy

Firmware for the Cellular Radio

5.5.3 Modem Firmware Update in the Pycom documentation contains a warning which, honestly, is ambiguous. Is a firmware always required or is it only required if upgrading to NB-IoT? I figure the conservative approach is to upgrade the firmware and try Cat-M1 first. If that's successful, we could go further and try NB-IoT. Again I've documented a more detailed procedure: link coming soon!


Opening Atom, the first thing we need to do is create a project folder. From the File menu, select Open Folder. Navigate to where you want to create your project and use the New Folder button to create a new folder. Open the new folder then click the Select Folder button.

Now we can connect to our GPy. Click the More button in the middle menu and then click Get serial ports. If you have only one serial device connected, you'll only see one. If you see multiple, disconnect the GPy and Get serial ports again; the one that disappeared is the one you need. Now go to Settings, Project settings. You'll need to ensure the address item at the top of the list is changed from "" to "COMx" where x is the number for the GPy you got from Get serial ports. Now, clicking in the bottom (black) box in Atom should get you connected. A "triple chevron" (three greater than signs) is what we're after.

import time
from machine import RTC
from network import LTE

NTP_SERVER = "au.pool.ntp.org"

# Need to use global variables.
# If in each function you delare a new reference, functionality is broken
lte = LTE()
rtc = RTC()

# Returns a network.LTE object with an active Internet connection.
def getLTE():

# If already used, the lte device will have an active connection.
# If not, need to set up a new connection.
if lte.isconnected():
return lte

# Modem does not connect successfully without first being reset.
print("Resetting LTE modem ... ", end='')

# While the configuration of the CGDCONT register survives resets,
# the other configurations don't. So just set them all up every time.
print("Configuring LTE ", end='')
print(".", end='')
lte.send_at_cmd('AT!="RRC::addscanfreq band=28 dl-earfcn=9410"')
print(".", end='')
print(" OK")

# If correctly configured for carrier network, attach() should succeed.
if not lte.isattached():
print("Attaching to LTE network ", end='')
if lte.isattached():
print(" OK")
print('.', end='')

# Once attached, connect() should succeed.
if not lte.isconnected():
print("Connecting on LTE network ", end='')
if lte.isconnected():
print(" OK")
print('.', end='')

# Once connect() succeeds, any call requiring Internet access will
# use the active LTE connection.
return lte

# Clean disconnection of the LTE network is required for future
# successful connections without a complete power cycle between.
def endLTE():

print("Disonnecting LTE ... ", end='')
print("Detaching LTE ... ", end='')

# Sets the internal real-time clock.
# Needs LTE for Internet access.
def setRTC():

# Ensures LTE session is connected before attempting NTP sync.
lte = getLTE()

print("Updating RTC from {} ".format(NTP_SERVER), end='')
while not rtc.synced():
print('.', end='')
print(' OK')

# Only returns an RTC object that has already been synchronised with an NTP server.
def getRTC():

if not rtc.synced():

return rtc

# Program starts here.
print("Initially, the RTC is {}".format("set" if rtc.synced() else "unset"))
rtc = getRTC()
print("RTC is {}".format(rtc.now() if rtc.synced() else "unset"))
except Exception:
pass # do nothing on error


In Atom, from the File menu, click New file. Once opened, save the file and make sure you give it a name ending .py. Atom will then colour your code to match the Python conventions. Copy and paste the above code file into the new file.

Jump to the bottom of the file – we really need to read this one bottom-up. The few lines after the comment # Program starts here are the crux of it. The first print line tells us if the real time clock (RTC) already has the date and time set. This is always true (or "set") if the power has remained connected since the date and time was last set. To see what the line rtc = getRTC() does, we need to go up a few lines to the definition of the getRTC() function: def getRTC():. The comment states the RTC object returned will already be synchronised with an NTP (network time protocol) server.

Think of this program as a set of steps. We're expecting that we're on the top step: that the RTC is set correctly. If not, we assume we're one step down: "it's time to set the RTC". If, on that second last step, we realise there's no Internet connection with which to set the RTC, we jump back another step and set up the Internet connection. This is a common programming pattern. Call a function that delivers a thing. If the thing hasn't been set up yet, do it immediately.

The try:, except: and finally: lines are an exception handling device. If the code between try: and except: fails, instead of the program stopping and printing out in Atom what went wrong, we'll pass instead of fixing the problem and always clean up by calling endLTE(). This shuts down the LTE device cleanly so it can start properly the next time we need it.

When first powered up, the clock (RTC) is not set. To set the clock an Internet connection is required. If the Internet is not already connected, and it won't be, the LTE modem is started up to provide the Internet connection. If everything goes to plan: the cellular device (LTE) gives us a connection to the Internet, which allows us to get to a network time protocol (NTP) server which is used to set the real time clock (RTC).

Below is the expected output if you run the code, stop it, then run it again. You can see that on the second run the RTC is still set from the first running. The LTE modem isn't started up because it's not required. This is ideal behaviour for an Internet of Things (IoT) device. The RTC's output is shown in it's unadulterated form: universal time coordinated (UTC) time.

>>> Running Z:\Content\Content2\Chris\Pycom\Pycom GPy\Code from Forum 3139\lte.py

Initially, the RTC is unset
Resetting LTE modem ... OK
Configuring LTE .. OK
Attaching to LTE network ... OK
Connecting on LTE network . OK
Updating RTC from au.pool.ntp.org . OK
RTC is (2018, 5, 28, 3, 52, 16, 45911, None)
RTC is (2018, 5, 28, 3, 52, 21, 48793, None)

>>> Running Z:\Content\Content2\Chris\Pycom\Pycom GPy\Code from Forum 3139\lte.py

Initially, the RTC is set
RTC is (2018, 5, 28, 3, 52, 32, 762544, None)
RTC is (2018, 5, 28, 3, 52, 37, 764759, None)


The most critical part of the program is the AT Command Set commands used to set up the LTE modem. I admit I don't understand how this works and I'm just glad there are people who do that are willing to share!

lte.send_at_cmd('AT!="RRC::addscanfreq band=28 dl-earfcn=9410"')

I owe timh a big thanks for helping me out on the Pycom Forum. I posted to the General LTE-M (Cat M1) connection problems thread and his answer was spot on.


I know I've assumed everything worked fine. But that was not my experience for most of the time I worked on this project. Here are some tidbits that might help you.

The most obvious thing that will cause your project to fail is that you have anything different to my assumptions, including:

  • Carrier is not Telstra
  • Carrier has no LTE Cat-M1 service
  • SIM card has no data service or does not have access to Cat-M1 service (test SIM with a phone)
  • Different version of firmware (all my firmware files latest at time of writing)
  • Poor coverage area for LTE
  • Using more than one reference to the same class causes failures. For example: if I have lte = LTE() more than once in my program it fails.

Where to go for help:

Routines that help:

  • Start at a known state. This gets tedious, but every time you want to try your code again: disconnect Atom, unplug the USB, wait a few seconds, plug it back in, reconnect Atom, run your code. Remember the RTC and LTE devices are independent. If they've freaked out about something the only way to get them back to normal is to power down.
  • Make small changes and test often. Rewriting a page of code is almost guaranteed to fail.
  • Reset the modem at the top of your program: lte.send_at_cmd('AT^RESET')
  • Print out the state of the LTE modem using:
  • When posting to forums include: firmware versions, code, output of the code, what you expect/want versus what happened.

My head is spinning with all the cool projects that are now possible with Pycom and cellular LTE data. A GPS "lojacking bug" is high on the list. If my bike goes missing, I want to be able to find it again!

The main thing I want you to get out of this project is this: upon completion you will have cellular data connected...

Have a question? Ask the Author of this guide today!