Arduino MKR NB 1500 now available on Mouser and Digikey


The Arduino MKR NB 1500 is now available for immediate shipment from and

I’ve had mine for 2 weeks now; surprised there is still no official release from the Arduino Store.

It would also be nice to have a separate listing under “MKR Boards” for it here.

Fair warning: No SMS capability yet through Arduino libraries. Lots to talk about there; board seems very buggy – maybe that’s the reason for the delay on the Arduino store side?

Also FYI: Like the 1400, the 1500 ships WITHOUT antenna and WITHOUT SIM.

Other than that, works well with Hologram SIM.

Connection on Cat-M is MUCH FASTER than 3G. Impressive.

Best of Luck, Ladies and Gentlemen.


What did you do/use to get yours working? I loaded the NTP demo and it just sits there telling me “Not connected”

I’m not sure if it’s a coverage issue, I thought I was ok in this area.



So, a couple of tricks to get the 1500 working with a Hologram SIM:

– Run the ChooseRadioAccessTechnology in the Examples. I used option 2 – CatM, with IoT fallback. Although this is ALLEGEDLY the default, 2 1500’s (new, fresh out of box) would not work until this was run.

– connectNB() is run with nothing specified --that’s right, no IP, no APN, nothing. Otherwise, it would not connect, at least for me.

From there, you should be good to go. Right now, I’ve only had luck connecting to AT&T. T-Mobile is NOT supported, and my Verizon coverage where I am is iffy.

Sketch below should get you going. Included UDP messaging. No, it is not elegant coding – just something quickly banged together to do basic testing on the NB 1500.

//  My First NB 1500 sketch:
//  -- Send messages to Hologram Cloud.
//  -- Receive messages via Hologram Dashboard (UDP).

#include <MKRNB.h>
#include <RTCZero.h>

String HOLOGRAM_DEVICE_KEY = "8digitdevicekey"; // Your 8-digit device key here, from Hologram Dashboard.
String HOLOGRAM_MESSAGE = "Test / sketch startup message";

unsigned long startMillis;
unsigned long currentMillis;
unsigned long period =  3600000; // 1-hour reporting interval.

int HOUR;    // Time of day (hours)
int MIN;     // Time of day (minutes)
int SEC;     // Time of day (seconds)
String MINS; // String of minutes with leading 0 if MIN < 10
String SECS; // String of seconds with leading 0 if SEC < 10

NB nb(1);  // 1 = debug mode - show AT commands.
NBClient client; // instance for web client.
GPRS gprs; // instance for GPRS.
NBUDP nbudp; // instance for UDP messaging.
RTCZero rtc; // instance for the Real Time Clock.

unsigned int localPort = 4010;
char packetBuffer[24]; // Buffer to hold incoming packet.

char server[] = "";
int port = 9999;
boolean connected = false;

int count = 1; // Loop interation counter.

void setup() {

  rtc.begin();     // Begin the Real Time Clock.
  nbudp.begin(4010); // Set port 4010 for UDP communication.
  nettime();  // Get the network time.
  transmit(); // Send the payload.
  startMillis = millis();  //initial start time for interval timer.

void loop() {  
///////////// GET UDP MESSAGE /////////////////////////////

int packetSize = nbudp.parsePacket();
    Serial.print("Received packet of size ");
    Serial.print("From ");
    IPAddress remote = nbudp.remoteIP();
    for (int i =0; i < 4; i++)
      Serial.print(remote[i], DEC);
      if (i < 3)
    Serial.print(", port ");

    // read the packet into packetBufffer,24);
    TRANSMIT_MESSAGE = packetBuffer;


currentMillis = millis();  // Get the current time
if (currentMillis - startMillis >= period) {   // Test whether the period has elapsed
Serial.println("Test passed -- sending report!");
startMillis = currentMillis;  // Update the timer start time to the current time.
nettime();      // Get the time from the cellular network.
transmit();  // Send payload to Hologram cloud.

delay(20000);  //  20-second loop to check for messages.
Serial.print("Looping... Loop Number "); Serial.println(count);

void nettime(){

  rtc.setEpoch(nb.getLocalTime());  // Get localized time from cell tower.  
  Serial.print("Unix time = ");

HOUR = rtc.getHours();
MIN =  rtc.getMinutes();
SEC =  rtc.getSeconds();

if (MIN < 10){                   // Add leading zero to Minutes if needed.
  MINS = ("0" + String(MIN));
  else { 
    MINS = (String(MIN));

if (SEC < 10){                   // Add leading zero to Seconds if needed.
  SECS = ("0" + String(SEC));
  else { 
    SECS = (String(SEC));

if(HOUR >= 13 && HOUR <= 23){
 HOLOGRAM_TIMESTAMP = (String(rtc.getMonth()) + "/" + String(rtc.getDay()) + "/" + String(rtc.getYear()) + ", " + String(rtc.getHours()-12) + ":" + MINS + ":" + SECS + " PM"); 
  HOLOGRAM_TIMESTAMP = (String(rtc.getMonth()) + "/" + String(rtc.getDay()) + "/" + String(rtc.getYear()) + ", " + String(rtc.getHours()) + ":" + MINS + ":" + SECS + " AM");
  Serial.println("*****************************");  // Debug for serial monitor
  Serial.println(HOLOGRAM_TIMESTAMP);               // Debug for serial monitor
  Serial.println("*****************************");  // Debug for serial monitor


void connectNB() {

 gprs.setTimeout(180000);  // TRY THE SAME TIMEOUTS AS USED WITH GSM.
   nb.setTimeout(180000);  // TRY THE SAME TIMEOUTS AS USED WITH GSM.
  boolean connected = false;

  while (!connected) {
    Serial.println("Begin NB Access");
    if ((nb.begin() == NB_READY) &&
        (gprs.attachGPRS() == GPRS_READY)) {
      connected = true;
      Serial.println("NB Access Success (nb.begin() and gprs.attachGPRS()).");
    else {
      Serial.println("Not connected");

void transmit(){
  if(gprs.attachGPRS() == GPRS_READY){
        Serial.println("GPRS ready!");
        else {
        Serial.println("GPRS not ready!");
        Serial.println("Reconnecting NB...");
 ///////////////// SEND PAYLOAD ////////////////////////////////////
   if (client.connect(server, port)) {
      client.print("{\"k\":\"" + HOLOGRAM_DEVICE_KEY + "\",\"d\":\"");
    else {
      MODEM.send("AT+USOER");  // Get socket error.

///////////////// SEND TIMESTAMP ///////////////////////////////////
     if (client.connect(server, port)) {
      client.print("{\"k\":\"" + HOLOGRAM_DEVICE_KEY + "\",\"d\":\"");
    else {
      MODEM.send("AT+USOER");  // Get socket error.


Hey Thanks MichaelM!!

After I sent that message I loaded pretty much all the example arduino programs except the ChooseRadioAccessTechnology app… Eventually I ended up on the SerialSARAPassthrough program and started banging some AT commands at the modem to see what I could figure out.

Ublox has a nice doc on AT commands

What I learned was that I could see the SIM:


+CCID: 894**************42


However when I asked for signal strength:


+CSQ: 99,99


The response is structured:

+CSQ: <signal_power>,<qual>

From the doc: When the RF power level of the received signal is the highest possible, the value 31 is reported. When it is not known, not detectable or currently not available, 99 is returned.

I checked the network registratyion


+CREG: 0,2

+CREG: <n>,<stat>

n: 0 (default value and factory-programmed value): network registration URC disabled

stat: 2: not registered, but the MT is currently searching a new operator to register to

So it really seems like just can’t find a network to connect to, but it was trying…

I spent maybe a couple hours loading programs and throwing AT commands at it, even tried a few different antennas and moving the thing around.

And then suddenly…


+CSQ: 22,99


+CREG: 0,5


+COPS: 0,0,"AT&T Hologram",8


What??? I was connected and it was working!!

A couple thoughts…

  1. (This one is probably important) Initially I forgot to hook up my battery and was only running of USB power, so I very well could have been underpowering the modem
  2. The default network is NOT set correctly as you mentioned (more below)
  3. Maybe it takes some time after you active your SIM for it to propgate to the various networks/systems?? I literally hit activate on this new SIM and tried to start running things

I never did end up running ChooseRadioAccessTechnology and if I query


+URAT: 7,8


From the docs: +URAT: <SelectedAcT>[,<PreferredAct>[,<2ndPreferredAct>]]

For SelectedAcT

Indicates the radio access technology and may be:
•0: GSM / GPRS / eGPRS (single mode)
•1: GSM / UMTS (dual mode)
•2: UMTS (single mode)
•3: LTE (single mode)
•4: GSM / UMTS / LTE (tri mode)
•5: GSM / LTE (dual mode)
•6: UMTS / LTE (dual mode)
•7: LTE Cat.M1
•8: LTE Cat.NB1
•9: GPRS / eGPRS

For PreferredAct

Indicates the preferred access technology; it is ignored if dual mode or tri mode arenot selected.
•0: GSM / GPRS / eGPRS
•3: LTE
•7: LTE Cat.M1
•8: LTE Cat.NB1
•9: GPRS / eGPRS

It definitely shows being connect to 7: LTE Cat.M1 but the preferred network is 8: LTE Cat.NB1

So yeah, it’s very possible this thing spent a long time trying to connect to a non-existing NB1 network here in the US and then eventually decided to try M1

NOTE For anyone reading this who wants to change the RAT via AT commands I saw this in the docs: Any change in the RAT selection must be done in the detached state issuing the AT+COPS=2 ATcommand. OR just use the example ChooseRadioAccessTechnology program

So thanks again MichaelM and good luck to anyone else who finds this!



Another quick updated, I did end up running the ChooseRadioAccessTechnology app and my results for AT+URAT? are still the same…

I looked at the source, and they are calling AT+URAT= and then sending two params.

It’s quite possible I’m reading the command sheet incorrectly but I believe it should be three params?

I tried setting it with three params manually




+URAT: 7,8



+URAT: 7,8


It still shows the same result…

So I was thoroughly confused and decided to read the docs closer, and sure enough:

SARA-R410M-02B / SARA-R410M-52B
•   <SelectedAcT> is the first AcT the module will search. In the case a suitable cell can't be found on thisAcT, the module will search the <PreferredAct> if defined.
•   <PreferredAct>  is  the  next  AcT  the  module  will  search  if  no  suitable  cell  can  be  found  on  the<SelectedAcT>.  In  the  case  a  suitable  cell  can't  be  found  on  this  AcT,  the  module  will  search  the<2ndPreferredAct> if defined.
•   <SelectedAcT>=0, 1, 2, 3, 4, 5, 6, 9 are not supported. The factory-programmed value of <SelectedAcT>is 7.
•   <PreferredAct>=0, 2, 3, 9 are not supported. The factory-programmed value of <PreferredAct> is 8.
•   <2ndPreferredAct>=0, 2, 3, 9 are not supported. The factory-programmed value of <PreferredAct> isNULL.

So the 02B variant which is what’s on the MKR NB 1500 I have behaves a little different

so I believe the response +URAT: 7,8 to be correct for picking M1 first and falling back on NB


Very glad to hear it worked out!

Yes, I have seen the same battery behavior you have. Although a battery should not be as necessary as it is on the 1400, I still find that USB power varies wildly depending on the source, so I typically run with a battery as a free “insurance policy“ when I’m trying to debug these darn things.

Cat him one still a bit of a new thing for everybody, so I’m still very much in the “figuring it out“ mode myself. I do know that it signs on much quicker now, after the SIM, the modem and the network all kind of get to know each other, then the 1400. I think we are all blazing new trails here!