Electron, international, 3rd party SIM, "PDP activation rejected"

I am trying to get things to work in a difficult situation here: Electron Europe/Asia variant, using a 3rd party SIM card (SORACOM), operating the device in South Korea.

Unfortunately, the device seems to never be able to connect.
Using the electron-clouddebug code, I am seeing the error sequence:
[ Modem::join ] = = = = = = = = = = = = = = = =
278.981 AT send 12 "AT+CGATT=1\r\n"
278.992 AT read OK 6 "\r\nOK\r\n"
278.992 AT send 14 "AT+UPSND=0,8\r\n"
279.002 AT read + 17 "\r\n+UPSND: 0,8,0\r\n"
279.012 AT read OK 6 "\r\nOK\r\n"
279.012 AT send 23 "AT+UPSD=0,7,“”\r\n"
279.023 AT read OK 6 "\r\nOK\r\n"
279.023 AT send 26 "AT+UPSD=0,1,“soracom.io”\r\n"
279.034 AT read OK 6 "\r\nOK\r\n"
279.034 AT send 15 "AT+UPSD=0,6,0\r\n"
279.044 AT read OK 6 "\r\nOK\r\n"
279.044 AT send 14 "AT+UPSDA=0,3\r\n"
281.794 AT read ERR 39 "\r\n+CME ERROR: PDP activation rejected\r\n"
281.795 AT send 15 "AT+UPSD=0,6,1\r\n"
281.795 AT read + 14 "\r\n+CIEV: 2,3\r\n"
281.806 AT read OK 6 "\r\nOK\r\n"
281.806 AT send 14 "AT+UPSDA=0,3\r\n"
285.356 AT read ERR 39 "\r\n+CME ERROR: PDP activation rejected\r\n"
285.357 AT send 15 "AT+UPSD=0,6,2\r\n"
285.357 AT read + 14 "\r\n+CIEV: 2,2\r\n"
285.368 AT read OK 6 "\r\nOK\r\n"
285.368 AT send 14 "AT+UPSDA=0,3\r\n"
289.188 AT read ERR 39 "\r\n+CME ERROR: PDP activation rejected\r\n"
Your modem APN/password/username may be wrong

A couple of things to note are: the SIM works OK in a cell phone (US iPhone 6S, unlocked) and can connect to a local cell provider.
I was able to have someone look at the operator-level logs, and they say that they are seeing a successful PDP context response, but that requests keep coming in from the device.

Any ideas?

Did you set the APN credentials?

Yes, using the STARTUP() macro.
As is visible in the log, soracom.io is the targeted APN.

Actually, after digging deeper, it seems like the SIM doesn’t actually work in the iPhone … it just shows the carrier logo and bars anyway, even if it can’t transmit any data.

So … I am going to check in with the MVNO and see what is up, fingers are pointing at them right now.

Issue suspended until further notice …

Hi @apullin I work closely with Soracom and have been able to get everything up and running. Here is a simple app which connects via Soracom Air and publishes events to Particle and also writes data to Soracom Harvest or Funnel (depending on the URL provided). OTA updates also work well, just be sure to set the speed class for your SIM to “s1.fast” before you perform the update (via the Soracom console or APIs). You can then perform an OTA update of your application via the Particle Build console or CLI.

#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <HttpClient.h>
#include "application.h"

// Set the soracom APN
STARTUP(cellular_credentials_set("soracom.io", "sora", "sora", NULL));
// Configuration

// device name
#define DEVICE_NAME "ElectronDHT"

// sensor type: [DHT11, DHT22, DHT21, AM2301]
#define DHTTYPE DHT11

// which digital pin for the DHT
#define DHTPIN D3

// which digital pin for the Photon/Spark Core/Electron LED
#define LEDPIN D7

// whether to use Farenheit instead of Celsius

// min/max values (sanity checks)

#define MIN_HUMIDITY 0
#define MAX_HUMIDITY 100

// sensor sending interval (seconds)
#define SEND_INTERVAL 60

// HTTP POST integration
#define HTTP_POST 1
#define HTTP_POST_HOST "harvest.soracom.io"
#define HTTP_POST_PORT 80
#define HTTP_POST_PATH "/"

// Particle event
#define PARTICLE_EVENT_NAME "electron-dht-logger-log"

// Integration Includes
#include "HttpClient/HttpClient.h"

// Locals
TCPClient tcpClient;


float humidity;
float temperature;
float heatIndex;

char humidityString[10];
char temperatureString[10];
char heatIndexString[10];

int failed = 0;

// last time since we sent sensor readings
int lastUpdate = 0;

HttpClient http;

// Headers currently need to be set at init, useful for API keys etc.
http_header_t httpHeaders[] = {
    { "Content-Type", "application/json" },
    { "Accept" , "*/*"},
    { NULL, NULL }

http_response_t response;
http_request_t request;

// for HTTP POST and Particle.publish payloads
char payload[1024];

 * Setup
void setup() {
    pinMode(LEDPIN, OUTPUT);
    digitalWrite(LEDPIN, HIGH);

    // configure Particle variables - float isn't accepted, so we have to use string versions
    Particle.variable("temperature", &temperatureString[0], STRING);
    Particle.variable("humidity", &humidityString[0], STRING);
    Particle.variable("heatIndex", &heatIndexString[0], STRING);

    Particle.variable("status", &failed, INT);

    // start the DHT sensor

    // startup event

    Spark.publish(PARTICLE_EVENT_NAME, payload, 60, PRIVATE);

    // run the first measurement

 * Event loop
void loop() {
    int now = Time.now();

    // only run every SEND_INTERVAL seconds
    if (now - lastUpdate < SEND_INTERVAL) {

    // turn on LED when updating
    digitalWrite(LEDPIN, HIGH);

    lastUpdate = now;

    failed = 0;

    // read humidity and temperature values
    humidity = dht.readHumidity();
    temperature = dht.readTemperature(USE_FARENHEIT);

    if (temperature == NAN
        || humidity == NAN
        || temperature > MAX_TEMPERATURE
        || temperature < MIN_TEMPERATURE
        || humidity > MAX_HUMIDITY
        || humidity < MIN_HUMIDITY) {
        // if any sensor failed, bail on updates
        failed = 1;
    } else {
        failed = 0;

        // calculate the heat index
        heatIndex = dht.computeHeatIndex(temperature, humidity, USE_FARENHEIT);

        // convert floats to strings for Particle variables
        sprintf(temperatureString, "%.2f", temperature);
        sprintf(humidityString, "%.2f", humidity);
        sprintf(heatIndexString, "%.2f", heatIndex);


        request.hostname = HTTP_POST_HOST;
        request.port = HTTP_POST_PORT;
        request.path = HTTP_POST_PATH;
        request.body = payload;

        http.post(request, response, httpHeaders);


    // done updating
    digitalWrite(LEDPIN, LOW);