Skip to content

Hardware

M2 IoT Modem

Specifications, pinout and set-up of the Thinger.io M2 IoT Modem, an M.2 module with ESP32, NB-IoT modem and 1NCE eSIM.

Reference6 min read

The M2 IoT Modem is a 22 × 42 mm module in M.2 2242 format that adds Wi-Fi, Bluetooth and NB-IoT to your own hardware. An ESP32-PICO-D4 runs your firmware and drives a Quectel BC660K-GL NB-IoT modem with a soldered 1NCE eSIM, so the whole module is programmed from one processor with the Arduino framework, ESP-IDF or MicroPython. It suits products that must connect where there is no Wi-Fi: city and environmental monitoring, industrial equipment, asset tracking, farming or home automation.

Front of the M2 IoT Modem with the ESP32-PICO-D4, the Quectel BC660K-GL modem and the two U.FL antenna connectors
M2 IoT Modem

Not for standard M.2 slots

The module uses the M.2 2242 mechanical format only. Its pinout is not compatible with the M.2 slots of laptops or SSD adapters: plugging it into one can damage the module and the host. Use it only with carrier boards designed for it.

Specifications ​

FeatureSpecification
Form factorM.2 2242 (22 × 42 mm), 4-layer PCB
ProcessorESP32-PICO-D4, dual-core Xtensa LX6 up to 240 MHz
Memory4 MB SPI flash, 520 KB SRAM, 448 KB ROM, 16 KB RTC SRAM
Wi-Fi802.11 b/g/n, 2.4 GHz
Bluetoothv4.2 BR/EDR and BLE
NB-IoT modemQuectel BC660K-GL (LTE Cat NB2), controlled with AT commands
NB-IoT bandsB1/2/3/4/5/8/12/13/17/18/19/20/25/28/66/70/85
SIM1NCE eSIM (MFF2), soldered on board
Antenna connectors2 × U.FL: WiFi-ANT (Wi-Fi and Bluetooth) and NB ANT (NB-IoT)
GPIO10 ESP32 GPIOs at the connector, plus UART0 and GPIO0
ProgrammingArduino framework, ESP-IDF or MicroPython, through UART0 of the ESP32
PeripheralsUART, SPI, I2C, I2S, LED PWM, pulse counter, GPIO, capacitive touch, ADC, DAC, TWAI

Power ​

PinVoltageUse
VBUS (pins 1 and 2)5 VPower input.
+3.3V (pins 38, 49 and 54)3.3 VOutput from the ESP32 supply rail, for references or low-power external circuits.

The modem supply is generated inside the module and is not available at the connector.

M2 IOT MODEMU.FL WiFi-ANTU.FL NB ANTESP32-PICO-D4Runs your firmwareWi-Fi and Bluetooth4 MB flashQuectel BC660K-GLNB-IoT modemAT commands1NCE eSIMsoldered (MFF2)UART TX: GPIO13 → modem RXDUART RX: GPIO4 ← modem TXDPWRKEY: GPIO23PSM_EINT: GPIO18RESET: GPIO5Control lines inverted: HIGH on the GPIO activates the modem inputUser LEDGPIO21M.2 2242 edge connector (to your carrier board)VBUS 5 V inUART0 TXD0, RXD0IO0, RESET (EN)10 free GPIOs+3.3V out, GNDProgram and debug the ESP32 through UART0: the module has no USB
Inside the M2 IoT Modem

LEDs ​

Position of the four LEDs on the M2 IoT Modem
LEDs of the module
LEDColorShows
LED1, modem powerRedOn while the BC660K-GL is powered.
LED2, network statusYellowNB-IoT network status, driven by the modem. The blinking pattern depends on the modem LED mode (AT+QLEDMODE).
LED3, TXBlueBlinks when the ESP32 sends data through UART0 (programming or serial console).
LED4, userRedFree for your firmware, on GPIO21. Set GPIO21 HIGH to turn it on.

Pinout ​

Schematic symbol of the M.2 B-key edge connector with the signal of each pin
M.2 edge connector

Pins not listed are not connected.

M.2 pinSignalESP32 GPIONotes
1, 2VBUS5 V power input.
3, 25, 40, 47, 51, 56, 63, 65, 67GNDGround.
4TXD0GPIO1UART0 TX: console and programming.
6RXD0GPIO3UART0 RX: console and programming.
8IO0GPIO0Boot mode. Hold it LOW at reset to enter the download mode.
10RESETENESP32 reset, active LOW.
20TxBC-RxESPGPIO4Modem TXD to the ESP32.
22RxBC-TxESPGPIO13ESP32 to the modem RXD.
24IO12GPIO12ADC2, touch. Strapping pin: do not drive it HIGH at boot.
26IO14GPIO14ADC2, touch.
28IO27GPIO27ADC2, touch.
30IO26GPIO26ADC2, DAC2.
32IO25GPIO25ADC2, DAC1.
34IO32GPIO32ADC1, touch.
36IO33GPIO33ADC1, touch.
38, 49, 54+3.3V3.3 V output.
42IO19GPIO19
44IO21GPIO21Shared with the user LED.
46IO22GPIO22
48PWRKEYGPIO23Modem power key.
50PSM_EINTGPIO18Wakes the modem from power saving mode.
52RESET_BCGPIO5Modem reset.
66BC_EXTERNAL_ENABLEExternal power control of the modem.
68, 70, 72, 74SIM_IO_EXT, SIM_RST_EXT, SIM_CLK_EXT, SIM_VDDConnected to the on-board eSIM. Do not connect another SIM to them.

Modem control lines ​

FunctionESP32 GPIOBehavior
Modem UART RXGPIO4Receives from the modem TXD, at 115200 baud by default.
Modem UART TXGPIO13Sends to the modem RXD.
PWRKEYGPIO23HIGH pulls the modem PWRKEY LOW (power on or off).
PSM_EINTGPIO18HIGH pulls the modem PSM_EINT LOW (wake up from power saving mode).
RESETGPIO5HIGH pulls the modem RESET LOW (hardware reset).

The control lines are inverted: a HIGH level on the GPIO activates the modem input. While the modem is in use, do not drive GPIO4, GPIO5, GPIO13, GPIO18 or GPIO23 from external hardware.

Connect it to Thinger.io ​

The ThingerNBCore class of the Thinger.io Arduino library connects the module over NB-IoT. It powers up the modem, applies the APN and network settings, waits for the network and keeps the connection to Thinger.io. To connect over Wi-Fi instead, use the module as any ESP32 board with ThingerESP32 (see Arduino).

Before you start ​

  • A carrier board for the module, powered through VBUS, and a USB-to-UART adapter on its TXD0 and RXD0 pins. The module has no USB interface.
  • Antennas on WiFi-ANT and NB ANT. Always connect the NB-IoT antenna before you use the modem.
  • NB-IoT coverage of the 1NCE network where you deploy the module. Coverage and bands depend on the country and the operator.
  • The Arduino IDE with the esp32 board package by Espressif Systems, or Visual Studio Code with PlatformIO.
  • The thinger.io library and a TinyGSM library with Quectel BC660 support. The TinyGSM release in the Arduino Library Manager does not support the BC660: install it from github.com/alvarolb/TinyGSM.
  • A device in Thinger.io, created as in Arduino. You need your username (YOUR_USER), the device ID (YOUR_DEVICE) and the device credentials (YOUR_TOKEN).

Upload the sketch ​

  1. Select the board

    In the Arduino IDE, select ESP32 Dev Module. In PlatformIO, use the pico32 board.

  2. Write the sketch

    Define THINGER_NB_M2CORE before the include, so the library uses the pins of this module. The 1NCE APN (iot.1nce.net, no user or password) is the default.

    main.cpp
    cpp
    // #define THINGER_SERVER "YOUR_SERVER"  // only on a private instance
    #define THINGER_SERIAL_DEBUG
    #define THINGER_NB_M2CORE  // pins of the M2 IoT Modem
    
    #include <ThingerNBCore.h>
    #include <ThingerESP32OTA.h>
    
    #define PIN_USER_LED 21
    
    ThingerNBCore thing("YOUR_USER", "YOUR_DEVICE", "YOUR_TOKEN");
    ThingerESP32OTA ota(thing, 512);
    
    void setup() {
      Serial.begin(115200);
      pinMode(PIN_USER_LED, OUTPUT);
    
      // power up and configure the modem
      while (!thing.init_modem()) {
        delay(1000);
      }
    
      // user LED, controlled from Thinger.io
      thing["led"] << digitalPin(PIN_USER_LED);
    
      // modem and network information
      thing["modem"] >> [](pson& out) {
        out["modem"] = thing.get_modem_info();
        out["IMEI"] = thing.get_modem_imei();
        out["CCID"] = thing.get_sim_iccid();
        out["operator"] = thing.get_operator();
      };
    }
    
    void loop() {
      thing.handle();
    }

    On the free Cloud account, leave the THINGER_SERVER line commented: the library connects to the free Cloud server by default. On a private instance, uncomment it and replace YOUR_SERVER with your domain.

  3. Upload it

    Hold GPIO0 (IO0) LOW while you reset the ESP32 with the RESET pin to enter the download mode, then upload the sketch. Open the serial monitor at 115200 baud to follow the connection.

The device shows as connected in Devices, with the led and modem device resources.

Network settings

Before init_modem(), set_apn() changes the APN for another SIM, set_preferred_bands() and set_preferred_operator() speed up the network search, and set_power_save(true) enables the modem power saving modes. The settings are stored in the modem and applied again only when they change.

To identify each module by the ICCID of its eSIM instead of a fixed device ID, see Use the SIM ICCID as device ID; on this module, keep #define THINGER_NB_M2CORE.

If the modem does not power on by itself, send a pulse on PWRKEY (GPIO23) before init_modem(). The timings are in the Quectel BC660K-GL hardware design document.

Resources ​

Disclaimer ​

INTERNET OF THINGER S.L. sells this module as a self-certified development kit, so it is not subject to the usual commercial homologation rules. The owner of the device is liable for any injury to third parties or damage to their property.