Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
kostiantynchertov
Published © GPL3+

TLS 1.3 for Arduino Nano

Building of secured device-to-cloud channel with Arduino Nano Every, Sim7000E NB-IoT Hat, and Luner IoT SIM with SAFE2 applet.

BeginnerProtip2,825
TLS 1.3 for Arduino Nano

Things used in this project

Hardware components

Arduino Nano Every
Arduino Nano Every
×1
Waveshare Sim7000E NB-IoT Hat
×1
DHT22 Temperature Sensor
DHT22 Temperature Sensor
×1
Luner IoT SIM
×1

Software apps and online services

Arduino IDE
Arduino IDE
Luner IoT Suite

Story

Read more

Schematics

Connection schema

Arduino Nano Every + Sim7000E + DHT22

Code

Demo Sketch

Arduino
#include "dht.h"


// Waveshare Sim7000E NB-IoT HAT
#define PIN_DTR  4

// AM2302 (DHT22)
#define PIN_DHT  5

// number of loops to wait for the end of initialization 
#define TECHNOLOGY_INIT_WAITING  7

#define LEN_MODEM_AT  4
const unsigned char MODEM_AT[LEN_MODEM_AT] = {'a', 't', '\r', '\n'};


#define LEN_MODEM_AT_CREG  10
const unsigned char MODEM_AT_CREG[LEN_MODEM_AT_CREG] = {'a', 't', '+', 'c', 'r', 'e', 'g', '?', '\r', '\n'};

#define LEN_MODEM_CREG  8
const char MODEM_CREG[LEN_MODEM_CREG] = {'+', 'C', 'R', 'E', 'G', ':', ' ', '\0'};

#define LEN_MODEM_AT_CSIM  8
const unsigned char MODEM_AT_CSIM[LEN_MODEM_AT_CSIM] = {'a', 't', '+', 'c', 's', 'i', 'm', '='};

#define LEN_MODEM_CSIM  8
const char MODEM_CSIM[LEN_MODEM_CSIM] = {'+', 'C', 'S', 'I', 'M', ':', ' ', '\0'};

#define LEN_APDU_HEADER  5

const char APDU_MANAGE_CHANNEL[LEN_APDU_HEADER] = { 0x00, 0x70, 0x00, 0x00, 0x00 };

const char APDU_SELECT[LEN_APDU_HEADER] = { 0x00, 0xa4, 0x04, 0x00, 0x00 };

#define LEN_APDU_PUT_DATA  5
const char APDU_PUT_DATA[LEN_APDU_PUT_DATA] = { 0x00, 0xda, 0x02, 0xc1, 0x00 };

#define LEN_AID_SAFE2  12
const char AID_SAFE2[LEN_AID_SAFE2] = {0xf0, 0x70, 0x6F, 0x64, 0x67, 0x73, 0x61, 0x66, 0x65, 0x32, 0x01, 0x01 };

#define LEN_MODEM_OK  4
const char MODEM_OK[LEN_MODEM_OK] = {'O', 'K', '\r', '\0'};

#define LEN_MODEM_ERROR  7
const char MODEM_ERROR[LEN_MODEM_ERROR] = {'E', 'R', 'R', 'O', 'R', '\r', '\0'};

#define LEN_1_CHAR_MAX  10
#define LEN_2_CHARS_MAX  100

#define CR   '\r'
#define EOL  '\n'

#define LEN_IO_BUFFER  255
char buf[LEN_IO_BUFFER];

#define LEN_DATA_BUF  4
unsigned char dataBuf[LEN_DATA_BUF];

unsigned char regFlag;

dht sensor;


char registered(char * resp) {
  char b = *resp;
  b &= 0x0f;
  resp++;
  if (*resp != CR) {
    // 2-chars <stat> value
    b *= 10;
    b += *resp - 0x30;
  }
  switch (b) {
    case 1: // 1: registered, home network
    case 5: // 5: registered, roaming
    case 6: // 6: registered for "SMS only", home network (applicable only when <AcTStatus>
            //    indicates E-UTRAN)
    case 7: // 7: registered for "SMS only", roaming (applicable only when <AcTStatus> indicates
            //    E-UTRAN)
    case 9: // 9: registered for "CSFB not preferred", home network (applicable only when
            //    <AcTStatus> indicates E-UTRAN)
    case 10:// 10:registered for "CSFB not preferred", roaming (applicable only when <AcTStatus>
            //    indicates E-UTRAN)
      return true;
    
    case 0: // 0: not registered, the MT is not currently searching a new operator to register to
    case 2: // 2: not registered, but the MT is currently searching a new operator to register to
    case 3: // 3: registration denied
    case 4: // 4: unknown (e.g. out of GERAN/UTRAN/E-UTRAN coverage)
    case 8: // 8: attached for emergency bearer services only (see 3GPP TS 24.008 [12] and 3GPP
            //    TS 24.301 [68] that specify the condition when the MS is considered as attached
            //    for emergency bearer services)
    default:
      return false;
  }
}

void logData(char * dataBuf, char len) {
  int dataLen = (unsigned char)len;
  int blockLen;
  int ofs = 0;
  while (dataLen > 0) {
    blockLen = Serial.availableForWrite();
    if (blockLen < 1) {
      delay(1);
      continue;
    }
    if (blockLen > dataLen) {
      blockLen = dataLen;
    }
    Serial.write(&dataBuf[ofs], blockLen);
    ofs += blockLen;
    dataLen -= blockLen;
  }
  Serial.flush();
}


void atCommand(char * cmdBuf, char len) {
  int cmdLen = (unsigned char)len;
  int blockLen;
  int ofs = 0;
  while (cmdLen > 0) {
    blockLen = Serial1.availableForWrite();
    if (blockLen < 1) {
      delay(1);
      continue;
    }
    if (blockLen > cmdLen) {
      blockLen = cmdLen;
    }
    Serial1.write(&cmdBuf[ofs], blockLen);
    ofs += blockLen;
    cmdLen -= blockLen;
  }
  Serial1.flush();
}

#define CNTR_MAX  10000

char atResponse(char * respBuf) {
  // read AT response
  char b = 0;
  short cntr = 0;
  char * ptr;
  int len = 0;
  int blockLen;
  //delay(10);
  // initialize for timeout
  respBuf[0] = 0;
  do {
    blockLen = Serial1.readBytesUntil(EOL, &respBuf[len], (int)(LEN_IO_BUFFER - len));
    if (blockLen > 0) {
      len += blockLen;
      respBuf[len++] = EOL;
      respBuf[len] = 0;
    }

    ptr = strstr(respBuf, MODEM_OK);
    if (ptr == NULL) {
      ptr = strstr(respBuf, MODEM_ERROR);
    }
    
  } while (ptr == NULL);
  return len;
}

char decimalPut(int v, char * dst) {
  char ofs = 0;
  // number of chars populated
  char cnt = 1;
  char b;
  // safety check - shall be redesigned for negative numbers
  if (v < 0) {
    v = 0;
  }
  if (v >= LEN_1_CHAR_MAX) {
    ofs++;
    cnt++;
  }
  if (v >= LEN_2_CHARS_MAX) {
    ofs++;
    cnt++;
  }
  // at least 1 digit shall be populated
  do {
    b = (char)(v % 10);
    dst[ofs--] = '0' + b;
    v -= b;
    v /= 10;
  } while (v > 0);
  return cnt;
}

const char HEX_CONVERSION[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

void hexPut(char b, char * dst) {
  dst[0] = HEX_CONVERSION[(b >> 4) & 0x0f];
  dst[1] = HEX_CONVERSION[b & 0x0f];
}

#define LEN_BYTE_HEX  2

char buildAtCsim(char * cmdBuf, char cla, const char * apduBuf, char * dataBuf, unsigned char dataLen) {
  int len = LEN_APDU_HEADER; 
  char ofs = 0;
  if (dataBuf != NULL) {
    len += dataLen;
  }
  memcpy(cmdBuf, MODEM_AT_CSIM, LEN_MODEM_AT_CSIM);
  ofs = LEN_MODEM_AT_CSIM;
  ofs += decimalPut(len << 1, &cmdBuf[ofs]);
  cmdBuf[ofs++] = ',';
  cmdBuf[ofs++] = '"';
  
  // put CLA
  hexPut(apduBuf[0] | cla, &cmdBuf[ofs]);
  ofs += LEN_BYTE_HEX;
  // put INS, P1, P2
  for (char i=1; i<4; i++) {
    hexPut(apduBuf[i], &cmdBuf[ofs]);
    ofs += LEN_BYTE_HEX;
  }
  // put LEN
  hexPut(dataLen, &cmdBuf[ofs]);
  ofs += LEN_BYTE_HEX;
  if ((dataLen > 0) && (dataBuf != NULL)) {
    // put Data
    for (char i=0; i<dataLen; i++) {
      hexPut(dataBuf[i], &cmdBuf[ofs]);
      ofs += LEN_BYTE_HEX;
    }
  }
  
  cmdBuf[ofs++] = '"';
  cmdBuf[ofs++] = '\r';
  cmdBuf[ofs++] = '\n';
  cmdBuf[ofs] = 0;
  return ofs;
}

#define ERR_INVALID_CSIM_RESPONSE  0x01
#define ERR_INVALID_OPEN_CHANNEL_RESPONSE  0x02
#define ERR_INVALID_CHANNEL_ID  0x03

#define OFS_APDU_P1  0x02
#define OFS_APDU_P2  0x03

#define MODE_CHANNEL_CLOSE  0x80



char send(unsigned char * dataPtr, char dataLen) {

  char cmdLen = 0;
  char respLen = 0;
  char chan = 0;
  // put AT SIM command: Open Supplementary Logical Channel
  //Serial.println("put 'at+csim: 00 70 00 00 01'");
  cmdLen = buildAtCsim(buf, 0, APDU_MANAGE_CHANNEL, NULL, 1);
  //Serial.println(buf);
  //Serial.flush();
  atCommand(buf, cmdLen);
  respLen = atResponse(buf);
  logData(buf, respLen);
  // analyze response
  char * ptr;
  ptr = strstr(buf, MODEM_CSIM);
  if (ptr == NULL) {
    Serial.println("CSIM not detected in the response");
    return ERR_INVALID_CSIM_RESPONSE;
  }

  if ((ptr[LEN_MODEM_CSIM - 1] != '6') ||
      (ptr[LEN_MODEM_CSIM + 0] != ',') || 
      (ptr[LEN_MODEM_CSIM + 1] != '"') ||
      (ptr[LEN_MODEM_CSIM + 2] != '0')) {

    Serial.println("Unexpected response for MANAGE CHANNEL");
    return ERR_INVALID_OPEN_CHANNEL_RESPONSE;
  }

  // extract channel ID
  chan = ptr[LEN_MODEM_CSIM + 3] - '0';
  if ((chan <= 0) || (chan >= 4)) {
    Serial.print("Unexpected response (chan ID) for MANAGE CHANNEL: ");
    Serial.println(chan);
    return ERR_INVALID_CHANNEL_ID;
  }
  // SELECT (by AID) SAFE2
  cmdLen = buildAtCsim(buf, chan, APDU_SELECT, (char *)AID_SAFE2, LEN_AID_SAFE2);
  //Serial.println(buf);
  //Serial.flush();
  atCommand(buf, cmdLen);
  respLen = atResponse(buf);
  logData(buf, respLen);
  
  // PUT DATA (from dataBuffer)
  cmdLen = buildAtCsim(buf, chan, APDU_PUT_DATA, (char *)dataPtr, dataLen);
  //Serial.println(buf);
  //Serial.flush();
  atCommand(buf, cmdLen);
  respLen = atResponse(buf);
  logData(buf, respLen);
  
  // close supplementary logical channel
  char apdu_close_channel[LEN_APDU_HEADER];
  memcpy(apdu_close_channel, APDU_MANAGE_CHANNEL, LEN_APDU_HEADER);
  apdu_close_channel[OFS_APDU_P1] = MODE_CHANNEL_CLOSE;
  apdu_close_channel[OFS_APDU_P2] = chan;
  cmdLen = buildAtCsim(buf, 0, apdu_close_channel, NULL, 0);
  //Serial.println(buf);
  //Serial.flush();
  atCommand(buf, cmdLen);
  respLen = atResponse(buf);
  logData(buf, respLen);
}


void setup() {
  // configure pins (DTR - mandatory)
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(PIN_DTR, OUTPUT);
  

  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect
  }
  Serial.println("nano started!");

  // wake up Sim7000 UART
  digitalWrite(PIN_DTR, LOW);   
  delay(1000);

  // set the data rate for the modem port
  Serial1.begin(57600);

  regFlag = false;
  char ready = 0;
  unsigned char b = 0;
  unsigned char pos = 0;
  do {
    // put AT command
    Serial.println("put 'at'");
    Serial1.write(MODEM_AT, LEN_MODEM_AT);
    delay(100);
    // modem could not respond during initialization
    if (Serial1.available()) {
      ready = 1;
      while (Serial1.available()) {
        b = Serial1.read();
        Serial.write(b);
        delay(10);
      }
    }
    delay(300);
  } while (ready == 0);

  unsigned char cntr = 0;
  while (cntr < TECHNOLOGY_INIT_WAITING) {
    // turn the LED on
    digitalWrite(LED_BUILTIN, HIGH);  
    // wait for a half of a second  
    delay(500);                       

    // put AT command
    Serial.println("put 'at+creg?'");
    atCommand(MODEM_AT_CREG, LEN_MODEM_AT_CREG);
    // read AT response
    pos = atResponse(buf);
    
    // turn the LED off by making the voltage LOW
    digitalWrite(LED_BUILTIN, LOW);    

    // analyze response
    char * ptr;
    ptr = strstr(buf, MODEM_CREG);
    if (ptr != NULL) {
      regFlag = registered(&ptr[LEN_MODEM_CREG + 1]);
      Serial.write(ptr[LEN_MODEM_CREG - 1]);
      Serial.write(':');
      Serial.write(ptr[LEN_MODEM_CREG + 1]);
      Serial.println();
    } else {
      Serial.println("not found `+CREG`");
    }
    if (regFlag) {
      cntr++;
    }
    Serial.print("cntr: ");
    Serial.println(cntr);
    // wait 1 sec before repeat
    delay(1000);
  }
  Serial.println("setup finished");
}

void loop() {
  
  if (sensor.read22(PIN_DHT) != DHTLIB_OK) {
  //float h = dht.readHumidity();
  //float t = dht.readTemperature();

  //if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from the sensor");
    delay(10000);
    return;
  }
  float h = sensor.humidity;
  float t = sensor.temperature;
  int ih = round(h);
  int it = round(t);

  Serial.print("Humidity: ");
  Serial.println(ih);
  Serial.print("Temperature: ");
  Serial.println(it);
  Serial.println();

  // put humidity as BigEndian short
  dataBuf[0] = (unsigned char)(ih >> 8);
  dataBuf[1] = (unsigned char)(ih & 0xff);
  // put Temperature as BigEndian short
  dataBuf[2] = (unsigned char)(it >> 8);
  dataBuf[3] = (unsigned char)(it & 0xff);

  // Dump sensor data
  char b;
  Serial.print("HT data:");
  for (byte i = 0; i < LEN_DATA_BUF; i++) {
    b = dataBuf[i];
    if (b < 0x10) {
      Serial.print("0");
    }
    Serial.print(b, HEX);
  } 
  Serial.println();
  
  send(&dataBuf[0], LEN_DATA_BUF);

  unsigned char minutes = 30;
  unsigned char secs;
  while (minutes > 0) {
    for (secs=0; secs < 60; secs++) {
      delay(1000);
    }
    minutes -= 1;
    Serial.print('.');
  }
}

Credits

kostiantynchertov
2 projects • 2 followers
Contact

Comments

Please log in or sign up to comment.