Cellular library for MTS Socket Modem Arduino Shield devices from Multi-Tech Systems

Dependents:   mtsas mtsas mtsas mtsas

Committer:
Vanger
Date:
Mon Aug 11 19:54:54 2014 +0000
Revision:
54:a6c738bfc391
Parent:
52:2cb58398a4f9
Child:
56:43205bd2752a
Added for loop check to ensure context is closed under EasyIP.cpp, added 100ms wait after leaving data mode with sendEscapeCommand() under EasyIP.cpp (due to AT command not being received by radio without slight wait)

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Vanger 26:2b769ed8de4f 1 #include "mbed.h"
Vanger 26:2b769ed8de4f 2 #include "EasyIP.h"
Vanger 26:2b769ed8de4f 3 #include "MTSText.h"
Vanger 26:2b769ed8de4f 4 #include "MTSLog.h"
Vanger 26:2b769ed8de4f 5 #include "CellUtils.h"
Vanger 26:2b769ed8de4f 6
Vanger 26:2b769ed8de4f 7 using namespace mts;
Vanger 26:2b769ed8de4f 8
Vanger 26:2b769ed8de4f 9 EasyIP::EasyIP(Radio type)
Vanger 26:2b769ed8de4f 10 {
Vanger 26:2b769ed8de4f 11 this->type = type;
Vanger 26:2b769ed8de4f 12 io = NULL;
Vanger 26:2b769ed8de4f 13 dcd = NULL;
Vanger 26:2b769ed8de4f 14 dtr = NULL;
Vanger 26:2b769ed8de4f 15 resetLine = NULL;
Vanger 26:2b769ed8de4f 16 echoMode = true;
Vanger 26:2b769ed8de4f 17 pppConnected = false;
Vanger 26:2b769ed8de4f 18 socketMode = TCP;
Vanger 26:2b769ed8de4f 19 socketOpened = false;
Vanger 26:2b769ed8de4f 20 socketCloseable = true;
Vanger 26:2b769ed8de4f 21 local_port = 0;
Vanger 26:2b769ed8de4f 22 local_address = "";
Vanger 26:2b769ed8de4f 23 host_port = 0;
Vanger 26:2b769ed8de4f 24 }
Vanger 26:2b769ed8de4f 25
Vanger 26:2b769ed8de4f 26 EasyIP::~EasyIP()
Vanger 26:2b769ed8de4f 27 {
Vanger 26:2b769ed8de4f 28 if (dtr != NULL) {
Vanger 26:2b769ed8de4f 29 dtr->write(1);
Vanger 26:2b769ed8de4f 30 }
Vanger 26:2b769ed8de4f 31
Vanger 26:2b769ed8de4f 32 delete dcd;
Vanger 26:2b769ed8de4f 33 delete dtr;
Vanger 26:2b769ed8de4f 34 delete resetLine;
Vanger 26:2b769ed8de4f 35 }
Vanger 26:2b769ed8de4f 36
Vanger 26:2b769ed8de4f 37 //Initializes the MTS IO Buffer
Vanger 26:2b769ed8de4f 38 bool EasyIP::init(MTSBufferedIO* io)
Vanger 26:2b769ed8de4f 39 {
Vanger 26:2b769ed8de4f 40 if (! Cellular::init(io)) {
Vanger 26:2b769ed8de4f 41 return false;
Vanger 26:2b769ed8de4f 42 }
Vanger 26:2b769ed8de4f 43
Vanger 26:2b769ed8de4f 44 logDebug("radio type: %s", Cellular::getRadioNames(type).c_str());
Vanger 49:1fc51c53cebf 45 //Turns on the HW flow control
Vanger 41:8b9b5098696f 46 if(sendBasicCommand("AT&K3", 2000) != MTS_SUCCESS) {
Vanger 52:2cb58398a4f9 47 logWarning("Failed to enable serial flow control");
Vanger 35:257eb41405e1 48 }
Vanger 26:2b769ed8de4f 49 return true;
Vanger 26:2b769ed8de4f 50 }
Vanger 26:2b769ed8de4f 51
Vanger 26:2b769ed8de4f 52 bool EasyIP::connect()
Vanger 26:2b769ed8de4f 53 {
Vanger 33:3b6f3904dde0 54 //Check if APN is not set, if it is not, connect will not work.
Vanger 29:edc613ed3f2e 55 if (type == MTSMC_H5_IP || type == MTSMC_H5 || type == MTSMC_G3) {
Vanger 29:edc613ed3f2e 56 if(apn.size() == 0) {
Vanger 29:edc613ed3f2e 57 logDebug("APN is not set");
Vanger 29:edc613ed3f2e 58 return false;
Vanger 29:edc613ed3f2e 59 }
Vanger 29:edc613ed3f2e 60 }
Vanger 29:edc613ed3f2e 61
Vanger 26:2b769ed8de4f 62 //Check if socket is open
Vanger 26:2b769ed8de4f 63 if(socketOpened) {
Vanger 26:2b769ed8de4f 64 return true;
Vanger 26:2b769ed8de4f 65 }
Vanger 26:2b769ed8de4f 66
Vanger 26:2b769ed8de4f 67 //Check if already connected
Vanger 26:2b769ed8de4f 68 if(isConnected()) {
Vanger 26:2b769ed8de4f 69 return true;
Vanger 26:2b769ed8de4f 70 }
Vanger 51:ffc556ba33f7 71
Vanger 26:2b769ed8de4f 72 Timer tmr;
Vanger 26:2b769ed8de4f 73 //Check Registration: AT+CREG? == 0,1
Vanger 26:2b769ed8de4f 74 tmr.start();
Vanger 26:2b769ed8de4f 75 do {
Vanger 26:2b769ed8de4f 76 Registration registration = getRegistration();
Vanger 26:2b769ed8de4f 77 if(registration != REGISTERED) {
Vanger 26:2b769ed8de4f 78 logTrace("Not Registered [%d] ... waiting", (int)registration);
Vanger 26:2b769ed8de4f 79 wait(1);
Vanger 26:2b769ed8de4f 80 } else {
Vanger 26:2b769ed8de4f 81 break;
Vanger 26:2b769ed8de4f 82 }
Vanger 30:1326b623919a 83 } while(tmr.read() < 30);
Vanger 51:ffc556ba33f7 84
Vanger 26:2b769ed8de4f 85 //Check RSSI: AT+CSQ
Vanger 26:2b769ed8de4f 86 tmr.reset();
Vanger 26:2b769ed8de4f 87 do {
Vanger 26:2b769ed8de4f 88 int rssi = getSignalStrength();
Vanger 26:2b769ed8de4f 89 logDebug("Signal strength: %d", rssi);
Vanger 51:ffc556ba33f7 90 if(rssi == 99 || rssi == -1) {
Vanger 26:2b769ed8de4f 91 logTrace("No Signal ... waiting");
Vanger 26:2b769ed8de4f 92 wait(1);
Vanger 26:2b769ed8de4f 93 } else {
Vanger 26:2b769ed8de4f 94 break;
Vanger 26:2b769ed8de4f 95 }
Vanger 26:2b769ed8de4f 96 } while(tmr.read() < 30);
Vanger 26:2b769ed8de4f 97
Vanger 52:2cb58398a4f9 98 //Make PPP connection
Vanger 26:2b769ed8de4f 99 if (type == MTSMC_H5 || type == MTSMC_G3) {
Vanger 26:2b769ed8de4f 100 logDebug("Making PPP Connection Attempt. APN[%s]", apn.c_str());
Vanger 26:2b769ed8de4f 101 } else {
Vanger 26:2b769ed8de4f 102 logDebug("Making PPP Connection Attempt");
Vanger 26:2b769ed8de4f 103 }
Vanger 35:257eb41405e1 104 std::string pppResult = sendCommand("AT#SGACT=1,1", 5000);
Vanger 30:1326b623919a 105 std::vector<std::string> parts;
Vanger 26:2b769ed8de4f 106 if(pppResult.find("OK") != std::string::npos) {
Vanger 30:1326b623919a 107 parts = Text::split(pppResult, "\r\n");
Vanger 26:2b769ed8de4f 108 if(parts.size() >= 2) {
Vanger 26:2b769ed8de4f 109 parts = Text::split(parts[1], " ");
Vanger 26:2b769ed8de4f 110 local_address = parts[1];
Vanger 26:2b769ed8de4f 111 }
Vanger 26:2b769ed8de4f 112 logInfo("PPP Connection Established: IP[%s]", local_address.c_str());
Vanger 26:2b769ed8de4f 113 pppConnected = true;
Vanger 26:2b769ed8de4f 114
Vanger 26:2b769ed8de4f 115 } else {
Vanger 30:1326b623919a 116 pppResult = sendCommand("AT#SGACT?", 2000);
Vanger 52:2cb58398a4f9 117 if(pppResult.find("1,1") != std::string::npos) {
Vanger 52:2cb58398a4f9 118 logDebug("Radio is already connected");
Vanger 52:2cb58398a4f9 119 pppConnected = true;
Vanger 30:1326b623919a 120 } else {
Vanger 52:2cb58398a4f9 121 logError("PPP connection attempt failed");
Vanger 52:2cb58398a4f9 122 pppConnected = false;
Vanger 30:1326b623919a 123 }
Vanger 26:2b769ed8de4f 124 }
Vanger 26:2b769ed8de4f 125
Vanger 26:2b769ed8de4f 126 return pppConnected;
Vanger 26:2b769ed8de4f 127 }
Vanger 26:2b769ed8de4f 128
Vanger 26:2b769ed8de4f 129 void EasyIP::disconnect()
Vanger 26:2b769ed8de4f 130 {
Vanger 52:2cb58398a4f9 131 //AT#SGACT=1,0: Close PPP connection
Vanger 52:2cb58398a4f9 132 logDebug("Closing PPP Connection");
Vanger 35:257eb41405e1 133 std::string result;
Vanger 35:257eb41405e1 134 Timer tmr;
Vanger 52:2cb58398a4f9 135
Vanger 52:2cb58398a4f9 136 if(socketOpened) {
Vanger 52:2cb58398a4f9 137 close();
Vanger 52:2cb58398a4f9 138 }
Vanger 52:2cb58398a4f9 139
Vanger 26:2b769ed8de4f 140 //Sends AT#SGACT=1,0 command
Vanger 54:a6c738bfc391 141 for (int y = 0; y < 5; y++) {
Vanger 54:a6c738bfc391 142 Code code = sendBasicCommand("AT#SGACT=1,0", 1000);
Vanger 54:a6c738bfc391 143 if (code == MTS_SUCCESS) {
Vanger 54:a6c738bfc391 144 logDebug("Successfully closed PPP Connection");
Vanger 54:a6c738bfc391 145 break;
Vanger 54:a6c738bfc391 146 }
Vanger 30:1326b623919a 147 }
Vanger 35:257eb41405e1 148
Vanger 52:2cb58398a4f9 149 /* Ensure PPP link is down, else ping commands will put radio in unknown state
Vanger 52:2cb58398a4f9 150 * (Link is not immediate in disconnection, even though OK is returned)
Vanger 51:ffc556ba33f7 151 */
Vanger 35:257eb41405e1 152 tmr.start();
Vanger 35:257eb41405e1 153 while(tmr.read() < 30) {
Vanger 35:257eb41405e1 154 result = sendCommand("AT#SGACT?", 1000);
Vanger 35:257eb41405e1 155 if(result.find("1,0") != std::string::npos) {
Vanger 35:257eb41405e1 156 break;
Vanger 35:257eb41405e1 157 } else if(result.find("ERROR") != std::string::npos) {
Vanger 35:257eb41405e1 158 break;
Vanger 35:257eb41405e1 159 } else {
Vanger 35:257eb41405e1 160 wait(1);
Vanger 35:257eb41405e1 161 }
Vanger 35:257eb41405e1 162 }
Vanger 35:257eb41405e1 163
Vanger 52:2cb58398a4f9 164 pppConnected = false;
Vanger 30:1326b623919a 165 return;
Vanger 26:2b769ed8de4f 166 }
Vanger 27:ec44d5a9544f 167
Vanger 26:2b769ed8de4f 168 bool EasyIP::isConnected()
Vanger 26:2b769ed8de4f 169 {
Vanger 52:2cb58398a4f9 170 enum RadioState {IDLE, CONNECTING, CONNECTED, DISCONNECTED};
Vanger 51:ffc556ba33f7 171 //state flags for various connection components
Vanger 30:1326b623919a 172 bool signal = false, regist = false, active = false;
Vanger 26:2b769ed8de4f 173
Vanger 29:edc613ed3f2e 174 //1) Check if APN was set if we're on an HSPA radio
Vanger 29:edc613ed3f2e 175 if (type == MTSMC_H5_IP || type == MTSMC_H5 || type == MTSMC_G3) {
Vanger 29:edc613ed3f2e 176 if(apn.size() == 0) {
Vanger 29:edc613ed3f2e 177 logDebug("APN is not set");
Vanger 29:edc613ed3f2e 178 return false;
Vanger 29:edc613ed3f2e 179 }
Vanger 29:edc613ed3f2e 180 }
Vanger 29:edc613ed3f2e 181
Vanger 29:edc613ed3f2e 182 //2) Check that we do not have a live connection up
Vanger 52:2cb58398a4f9 183 if (socketOpened) {
Vanger 29:edc613ed3f2e 184 logDebug("Socket is opened");
Vanger 29:edc613ed3f2e 185 return true;
Vanger 29:edc613ed3f2e 186 }
Vanger 29:edc613ed3f2e 187
Vanger 29:edc613ed3f2e 188 //3) Query the radio
Vanger 52:2cb58398a4f9 189 int rssi = getSignalStrength();
Vanger 52:2cb58398a4f9 190 if (rssi == 99 || rssi == -1) {
Vanger 52:2cb58398a4f9 191 //Signal strength is nonexistent
Vanger 29:edc613ed3f2e 192 signal = false;
Vanger 29:edc613ed3f2e 193 } else {
Vanger 52:2cb58398a4f9 194 signal = true;
Vanger 29:edc613ed3f2e 195 }
Vanger 27:ec44d5a9544f 196
Vanger 52:2cb58398a4f9 197 Registration creg = getRegistration();
Vanger 52:2cb58398a4f9 198 if (creg == REGISTERED) {
Vanger 52:2cb58398a4f9 199 regist = true;
Vanger 29:edc613ed3f2e 200 } else {
Vanger 52:2cb58398a4f9 201 regist = false;
Vanger 29:edc613ed3f2e 202 }
Vanger 27:ec44d5a9544f 203
Vanger 52:2cb58398a4f9 204 string reply = sendCommand("AT#SGACT?", 500);
Vanger 52:2cb58398a4f9 205 if (reply.find("1,1") != std::string::npos) {
Vanger 52:2cb58398a4f9 206 active = true;
Vanger 29:edc613ed3f2e 207 } else {
Vanger 52:2cb58398a4f9 208 active = false;
Vanger 29:edc613ed3f2e 209 }
Vanger 52:2cb58398a4f9 210
Vanger 52:2cb58398a4f9 211 RadioState state;
Vanger 52:2cb58398a4f9 212 bool ppp = pppConnected;
Vanger 52:2cb58398a4f9 213 if (signal && regist && active) {
Vanger 52:2cb58398a4f9 214 //Radio connected
Vanger 52:2cb58398a4f9 215 state = CONNECTED;
Vanger 52:2cb58398a4f9 216 pppConnected = true;
Vanger 52:2cb58398a4f9 217 } else if (signal && !regist && !active) {
Vanger 52:2cb58398a4f9 218 //Radio idle
Vanger 52:2cb58398a4f9 219 state = IDLE;
Vanger 52:2cb58398a4f9 220 pppConnected = false;
Vanger 52:2cb58398a4f9 221 } else if (active) {
Vanger 52:2cb58398a4f9 222 //Radio Connecting
Vanger 52:2cb58398a4f9 223 state = CONNECTING;
Vanger 52:2cb58398a4f9 224 } else {
Vanger 52:2cb58398a4f9 225 //Radio Disconnected
Vanger 52:2cb58398a4f9 226 state = DISCONNECTED;
Vanger 30:1326b623919a 227 pppConnected = false;
Vanger 30:1326b623919a 228 }
Vanger 35:257eb41405e1 229
Vanger 52:2cb58398a4f9 230 if (!ppp && state == CONNECTED) {
Vanger 52:2cb58398a4f9 231 logWarning("Internal PPP state tracking differs from radio (DISCONNECTED:CONNECTED)");
Vanger 52:2cb58398a4f9 232 } else if (ppp && state != CONNECTED) {
Vanger 52:2cb58398a4f9 233 logWarning("Internal PPP state tracking differs from radio (CONNECTED:%s)", state);
Vanger 29:edc613ed3f2e 234 }
Vanger 52:2cb58398a4f9 235
Vanger 26:2b769ed8de4f 236 return pppConnected;
Vanger 26:2b769ed8de4f 237 }
Vanger 30:1326b623919a 238
Vanger 33:3b6f3904dde0 239 void EasyIP::reset()
Vanger 33:3b6f3904dde0 240 {
Vanger 33:3b6f3904dde0 241 disconnect();
Vanger 52:2cb58398a4f9 242
Vanger 33:3b6f3904dde0 243 if(sendBasicCommand("AT#REBOOT", 10000) != MTS_SUCCESS) {
Vanger 52:2cb58398a4f9 244 logError("Socket Modem did not accept RESET command");
Vanger 33:3b6f3904dde0 245 } else {
Vanger 52:2cb58398a4f9 246 logWarning("Socket Modem is resetting, allow 30 seconds for it to come back");
Vanger 33:3b6f3904dde0 247 return;
Vanger 33:3b6f3904dde0 248 }
Vanger 33:3b6f3904dde0 249 }
Vanger 33:3b6f3904dde0 250
Vanger 26:2b769ed8de4f 251 //Binds the socket to a specific port if able
Vanger 26:2b769ed8de4f 252 bool EasyIP::bind(unsigned int port)
Vanger 26:2b769ed8de4f 253 {
Vanger 30:1326b623919a 254 if(socketOpened) {
Vanger 30:1326b623919a 255 logError("socket is open. Can not set local port");
Vanger 30:1326b623919a 256 return false;
Vanger 30:1326b623919a 257 }
Vanger 30:1326b623919a 258 if(port > 65535) {
Vanger 30:1326b623919a 259 logError("port out of range (0-65535)");
Vanger 30:1326b623919a 260 return false;
Vanger 30:1326b623919a 261 }
Vanger 30:1326b623919a 262 local_port = port;
Vanger 26:2b769ed8de4f 263 return true;
Vanger 26:2b769ed8de4f 264 }
Vanger 27:ec44d5a9544f 265
Vanger 26:2b769ed8de4f 266 bool EasyIP::open(const std::string& address, unsigned int port, Mode mode)
Vanger 26:2b769ed8de4f 267 {
Vanger 30:1326b623919a 268 char sOpenSocketCmd[256] = {0}; //String for AT command
Vanger 30:1326b623919a 269 std::string sMode = "";
Vanger 31:529db15abda7 270 int typeSocket = 0;
Vanger 31:529db15abda7 271 int closeType = 0;
Vanger 30:1326b623919a 272
Vanger 30:1326b623919a 273 //1) Check that we do not have a live connection up
Vanger 30:1326b623919a 274 if(socketOpened) {
Vanger 30:1326b623919a 275 //Check that the address, port, and mode match
Vanger 30:1326b623919a 276 if(host_address != address || host_port != port || socketMode != mode) {
Vanger 30:1326b623919a 277 if(socketMode == TCP) {
Vanger 30:1326b623919a 278 logError("TCP socket already opened [%s:%d]", host_address.c_str(), host_port);
Vanger 30:1326b623919a 279 } else {
Vanger 30:1326b623919a 280 logError("UDP socket already opened [%s:%d]", host_address.c_str(), host_port);
Vanger 30:1326b623919a 281 }
Vanger 30:1326b623919a 282 return false;
Vanger 30:1326b623919a 283 }
Vanger 30:1326b623919a 284
Vanger 30:1326b623919a 285 logDebug("Socket already opened");
Vanger 30:1326b623919a 286 return true;
Vanger 30:1326b623919a 287 }
Vanger 30:1326b623919a 288
Vanger 30:1326b623919a 289 //2) Check Parameters
Vanger 30:1326b623919a 290 if(port > 65535) {
Vanger 30:1326b623919a 291 logError("port out of range (0-65535)");
Vanger 30:1326b623919a 292 return false;
Vanger 30:1326b623919a 293 }
Vanger 41:8b9b5098696f 294
Vanger 41:8b9b5098696f 295 if(type == MTSMC_EV3) {
Vanger 41:8b9b5098696f 296 if(!local_port) {
Vanger 41:8b9b5098696f 297 logDebug("Local port set to 1, port 0 not supported for MTSMC_EV3");
Vanger 41:8b9b5098696f 298 local_port = 1;
Vanger 41:8b9b5098696f 299 }
Vanger 41:8b9b5098696f 300 }
Vanger 30:1326b623919a 301
Vanger 30:1326b623919a 302 //3) Check PPP connection
Vanger 30:1326b623919a 303 if(!isConnected()) {
Vanger 30:1326b623919a 304 logError("PPP not established. Attempting to connect");
Vanger 30:1326b623919a 305 if(!connect()) {
Vanger 30:1326b623919a 306 logError("PPP connection failed");
Vanger 30:1326b623919a 307 return false;
Vanger 30:1326b623919a 308 } else {
Vanger 30:1326b623919a 309 logDebug("PPP connection established");
Vanger 30:1326b623919a 310 }
Vanger 30:1326b623919a 311 }
Vanger 30:1326b623919a 312
Vanger 30:1326b623919a 313 //4) Set escape sequence to not be transmitted
Vanger 30:1326b623919a 314 if(sendBasicCommand("AT#SKIPESC=1", 2000) != MTS_SUCCESS) {
Vanger 30:1326b623919a 315 logWarning("Failed to disable escape sequence transmission on data mode suspension");
Vanger 30:1326b623919a 316 }
Vanger 30:1326b623919a 317
Vanger 30:1326b623919a 318 if(mode == TCP) {
Vanger 30:1326b623919a 319 typeSocket = 0;
Vanger 30:1326b623919a 320 sMode = "TCP";
Vanger 30:1326b623919a 321 } else {
Vanger 30:1326b623919a 322 typeSocket = 1;
Vanger 30:1326b623919a 323 sMode = "UDP";
Vanger 30:1326b623919a 324 }
Vanger 31:529db15abda7 325
Vanger 31:529db15abda7 326 if(socketCloseable) {
Vanger 31:529db15abda7 327 closeType = 0;
Vanger 31:529db15abda7 328 } else {
Vanger 31:529db15abda7 329 closeType = 255;
Vanger 31:529db15abda7 330 }
Vanger 31:529db15abda7 331 //5) Open Socket
Vanger 31:529db15abda7 332 sprintf(sOpenSocketCmd, "AT#SD=1,%d,%d,%s,%d,%d,0", typeSocket, port, address.c_str(),closeType , local_port);
Vanger 38:b2088faa8bfd 333 std::string response = sendCommand(sOpenSocketCmd, 60000);
Vanger 30:1326b623919a 334
Vanger 30:1326b623919a 335 if(response.find("CONNECT") != std::string::npos) {
Vanger 30:1326b623919a 336 host_address = address;
Vanger 30:1326b623919a 337 host_port = port;
Vanger 30:1326b623919a 338
Vanger 30:1326b623919a 339 logInfo("Opened %s Socket [%s:%d]", sMode.c_str(), address.c_str(), port);
Vanger 30:1326b623919a 340 socketOpened = true;
Vanger 30:1326b623919a 341 socketMode = mode;
Vanger 30:1326b623919a 342 } else {
Vanger 30:1326b623919a 343 logWarning("Unable to open %s Socket [%s:%d]", sMode.c_str(), address.c_str(), port);
Vanger 30:1326b623919a 344 socketOpened = false;
Vanger 30:1326b623919a 345 }
Vanger 30:1326b623919a 346
Vanger 26:2b769ed8de4f 347 return socketOpened;
Vanger 26:2b769ed8de4f 348 }
Vanger 27:ec44d5a9544f 349
Vanger 26:2b769ed8de4f 350 bool EasyIP::isOpen()
Vanger 26:2b769ed8de4f 351 {
Vanger 30:1326b623919a 352 if(io->readable()) {
Vanger 52:2cb58398a4f9 353 logDebug("Assuming open, data available to read.");
Vanger 30:1326b623919a 354 return true;
Vanger 30:1326b623919a 355 }
Vanger 26:2b769ed8de4f 356 return socketOpened;
Vanger 26:2b769ed8de4f 357 }
Vanger 27:ec44d5a9544f 358
Vanger 26:2b769ed8de4f 359 bool EasyIP::close()
Vanger 26:2b769ed8de4f 360 {
Vanger 26:2b769ed8de4f 361
Vanger 30:1326b623919a 362 if(io == NULL) {
Vanger 30:1326b623919a 363 logError("MTSBufferedIO not set");
Vanger 30:1326b623919a 364 return false;
Vanger 30:1326b623919a 365 }
Vanger 30:1326b623919a 366
Vanger 30:1326b623919a 367 if(!socketOpened) {
Vanger 30:1326b623919a 368 logWarning("Socket close() called, but socket was not open");
Vanger 30:1326b623919a 369 return true;
Vanger 30:1326b623919a 370 }
Vanger 30:1326b623919a 371
Vanger 30:1326b623919a 372 if(!socketCloseable) {
Vanger 30:1326b623919a 373 logError("Socket is not closeable");
Vanger 30:1326b623919a 374 return false;
Vanger 30:1326b623919a 375 }
Vanger 30:1326b623919a 376
Vanger 30:1326b623919a 377 if(!sendEscapeCommand()) {
Vanger 30:1326b623919a 378 logError("Failed to exit online mode");
Vanger 30:1326b623919a 379 return false;
Vanger 30:1326b623919a 380 } else {
Vanger 30:1326b623919a 381 socketOpened = false;
Vanger 30:1326b623919a 382 }
Vanger 30:1326b623919a 383
Vanger 54:a6c738bfc391 384 if (sendBasicCommand("AT#SH=1", 2000) != MTS_SUCCESS) {
Vanger 30:1326b623919a 385 logDebug("Failed to close socket connection");
Vanger 30:1326b623919a 386 }
Vanger 30:1326b623919a 387
Vanger 30:1326b623919a 388 Timer tmr;
Vanger 30:1326b623919a 389 int counter = 0;
Vanger 30:1326b623919a 390 char tmp[256];
Vanger 30:1326b623919a 391 tmr.start();
Vanger 30:1326b623919a 392 do {
Vanger 30:1326b623919a 393 if(socketOpened == false) {
Vanger 30:1326b623919a 394 break;
Vanger 30:1326b623919a 395 }
Vanger 30:1326b623919a 396 read(tmp, 256, 1000);
Vanger 30:1326b623919a 397 } while(counter++ < 10);
Vanger 30:1326b623919a 398
Vanger 30:1326b623919a 399 io->rxClear();
Vanger 30:1326b623919a 400 io->txClear();
Vanger 30:1326b623919a 401
Vanger 30:1326b623919a 402 return !socketOpened;
Vanger 26:2b769ed8de4f 403 }
Vanger 26:2b769ed8de4f 404
Vanger 26:2b769ed8de4f 405 int EasyIP::read(char* data, int max, int timeout)
Vanger 26:2b769ed8de4f 406 {
Vanger 30:1326b623919a 407 if(io == NULL) {
Vanger 30:1326b623919a 408 logError("MTSBufferedIO not set");
Vanger 30:1326b623919a 409 return -1;
Vanger 30:1326b623919a 410 }
Vanger 30:1326b623919a 411
Vanger 30:1326b623919a 412 //Check that nothing is in the rx buffer
Vanger 30:1326b623919a 413 if(!socketOpened && !io->readable()) {
Vanger 30:1326b623919a 414 logError("Socket is not open");
Vanger 30:1326b623919a 415 return -1;
Vanger 30:1326b623919a 416 }
Vanger 30:1326b623919a 417
Vanger 30:1326b623919a 418 int bytesRead = 0;
Vanger 30:1326b623919a 419
Vanger 30:1326b623919a 420
Vanger 30:1326b623919a 421 if(timeout >= 0) {
Vanger 30:1326b623919a 422 bytesRead = io->read(data, max, static_cast<unsigned int>(timeout));
Vanger 30:1326b623919a 423 } else {
Vanger 30:1326b623919a 424 bytesRead = io->read(data, max);
Vanger 30:1326b623919a 425 }
Vanger 35:257eb41405e1 426
Vanger 35:257eb41405e1 427 //Scan for socket closed message
Vanger 31:529db15abda7 428 if(bytesRead > 0 && socketCloseable) {
Vanger 35:257eb41405e1 429 for(int i = 0; i < bytesRead; i++) {
Vanger 31:529db15abda7 430 if(data[i] == 'N') {
Vanger 31:529db15abda7 431 if(strstr(&data[i], "NO CARRIER")) {
Vanger 31:529db15abda7 432 logTrace("Found socket closed message. Checking validity");
Vanger 31:529db15abda7 433 //Close socket and Cut Off End of Message
Vanger 31:529db15abda7 434 socketOpened = socketCheck(); //Verifies legitimacy of socket disconnect
Vanger 31:529db15abda7 435 if(socketOpened) {
Vanger 31:529db15abda7 436 logDebug("Socket still open");
Vanger 31:529db15abda7 437 continue;
Vanger 31:529db15abda7 438 } else {
Vanger 31:529db15abda7 439 logDebug("Socket closed");
Vanger 31:529db15abda7 440 data[i] = '\0';
Vanger 31:529db15abda7 441 bytesRead = i;
Vanger 31:529db15abda7 442 break;
Vanger 31:529db15abda7 443 }
Vanger 30:1326b623919a 444 }
Vanger 30:1326b623919a 445 }
Vanger 30:1326b623919a 446 }
Vanger 30:1326b623919a 447 }
Vanger 30:1326b623919a 448 return bytesRead;
Vanger 26:2b769ed8de4f 449 }
Vanger 26:2b769ed8de4f 450
Vanger 26:2b769ed8de4f 451 int EasyIP::write(const char* data, int length, int timeout)
Vanger 26:2b769ed8de4f 452 {
Vanger 30:1326b623919a 453 if(io == NULL) {
Vanger 30:1326b623919a 454 logError("MTSBufferedIO not set");
Vanger 30:1326b623919a 455 return -1;
Vanger 30:1326b623919a 456 }
Vanger 30:1326b623919a 457
Vanger 30:1326b623919a 458 if(!socketOpened) {
Vanger 30:1326b623919a 459 logError("Socket is not open");
Vanger 30:1326b623919a 460 return -1;
Vanger 30:1326b623919a 461 }
Vanger 30:1326b623919a 462
Vanger 30:1326b623919a 463 int bytesWritten = 0;
Vanger 31:529db15abda7 464 int size = length;
Vanger 31:529db15abda7 465 int failedWrites = 0;
Vanger 30:1326b623919a 466 if(timeout >= 0) {
Vanger 30:1326b623919a 467 Timer tmr;
Vanger 30:1326b623919a 468 tmr.start();
Vanger 30:1326b623919a 469 do {
Vanger 30:1326b623919a 470 int available = io->writeable();
Vanger 30:1326b623919a 471 if (available > 0) {
Vanger 31:529db15abda7 472 size = MIN(available, length - bytesWritten);
Vanger 31:529db15abda7 473 bytesWritten += io->write(&data[bytesWritten], size);
Vanger 30:1326b623919a 474 } else {
Vanger 30:1326b623919a 475 wait(0.05);
Vanger 30:1326b623919a 476 }
Vanger 30:1326b623919a 477 } while (tmr.read_ms() <= timeout && bytesWritten < length);
Vanger 30:1326b623919a 478 } else {
Vanger 31:529db15abda7 479 //If timeout is -1:
Vanger 31:529db15abda7 480 do {
Vanger 31:529db15abda7 481 int available = io->writeable();
Vanger 31:529db15abda7 482 if(available > 0) {
Vanger 31:529db15abda7 483 size = MIN(available, length - bytesWritten);
Vanger 31:529db15abda7 484 int currentWritten = io->write(&data[bytesWritten], size);
Vanger 31:529db15abda7 485 bytesWritten += currentWritten;
Vanger 31:529db15abda7 486 if(!currentWritten) {
Vanger 31:529db15abda7 487 failedWrites++;
Vanger 31:529db15abda7 488 }
Vanger 31:529db15abda7 489 if(failedWrites > 10) {
Vanger 31:529db15abda7 490 logError("Couldn't write any characters");
Vanger 31:529db15abda7 491 return bytesWritten;
Vanger 31:529db15abda7 492 }
Vanger 31:529db15abda7 493 } else {
Vanger 31:529db15abda7 494 wait(0.05);
Vanger 30:1326b623919a 495 }
Vanger 31:529db15abda7 496 } while (bytesWritten < length);
Vanger 30:1326b623919a 497 }
Vanger 30:1326b623919a 498 return bytesWritten;
Vanger 26:2b769ed8de4f 499 }
Vanger 26:2b769ed8de4f 500
Vanger 26:2b769ed8de4f 501 unsigned int EasyIP::readable()
Vanger 26:2b769ed8de4f 502 {
Vanger 30:1326b623919a 503 if(io == NULL) {
Vanger 30:1326b623919a 504 logWarning("MTSBufferedIO not set");
Vanger 30:1326b623919a 505 return 0;
Vanger 30:1326b623919a 506 }
Vanger 30:1326b623919a 507 if(!socketOpened && !io->readable()) {
Vanger 30:1326b623919a 508 logWarning("Socket is not open");
Vanger 30:1326b623919a 509 return 0;
Vanger 30:1326b623919a 510 }
Vanger 26:2b769ed8de4f 511 return io->readable();
Vanger 26:2b769ed8de4f 512 }
Vanger 26:2b769ed8de4f 513
Vanger 26:2b769ed8de4f 514 unsigned int EasyIP::writeable()
Vanger 26:2b769ed8de4f 515 {
Vanger 30:1326b623919a 516 if(io == NULL) {
Vanger 30:1326b623919a 517 logWarning("MTSBufferedIO not set");
Vanger 30:1326b623919a 518 return 0;
Vanger 30:1326b623919a 519 }
Vanger 30:1326b623919a 520 if(!socketOpened) {
Vanger 30:1326b623919a 521 logWarning("Socket is not open");
Vanger 30:1326b623919a 522 return 0;
Vanger 30:1326b623919a 523 }
Vanger 26:2b769ed8de4f 524
Vanger 26:2b769ed8de4f 525 return io->writeable();
Vanger 26:2b769ed8de4f 526 }
Vanger 26:2b769ed8de4f 527
Vanger 26:2b769ed8de4f 528 bool EasyIP::setDeviceIP(std::string address)
Vanger 26:2b769ed8de4f 529 {
Vanger 26:2b769ed8de4f 530 if (address.compare("DHCP") == 0) {
Vanger 26:2b769ed8de4f 531 return true;
Vanger 26:2b769ed8de4f 532 } else {
Vanger 52:2cb58398a4f9 533 logWarning("Radio does not support static IPs, using DHCP.");
Vanger 26:2b769ed8de4f 534 return false;
Vanger 26:2b769ed8de4f 535 }
Vanger 26:2b769ed8de4f 536 }
Vanger 26:2b769ed8de4f 537
Vanger 26:2b769ed8de4f 538 Code EasyIP::setApn(const std::string& apn)
Vanger 26:2b769ed8de4f 539 {
Vanger 26:2b769ed8de4f 540 if (type == MTSMC_H5 || type == MTSMC_G3) {
Vanger 26:2b769ed8de4f 541 //Set IP,PPP,IPv6
Vanger 26:2b769ed8de4f 542 Code code = sendBasicCommand("AT+CGDCONT=1,PPP," + apn, 1000);
Vanger 26:2b769ed8de4f 543 if (code != MTS_SUCCESS) {
Vanger 33:3b6f3904dde0 544 return code; //This will return whatever is not MTS_SUCCESS
Vanger 26:2b769ed8de4f 545 }
Vanger 26:2b769ed8de4f 546 this->apn = apn;
Vanger 26:2b769ed8de4f 547 return code; //This will return MTS_SUCCESS
Vanger 26:2b769ed8de4f 548 } else {
Vanger 26:2b769ed8de4f 549 logInfo("CDMA radios don't need an APN");
Vanger 26:2b769ed8de4f 550 return MTS_SUCCESS;
Vanger 26:2b769ed8de4f 551 }
Vanger 26:2b769ed8de4f 552 }
Vanger 27:ec44d5a9544f 553
Vanger 26:2b769ed8de4f 554 std::string EasyIP::getDeviceIP()
Vanger 26:2b769ed8de4f 555 {
Vanger 26:2b769ed8de4f 556 return local_address;
Vanger 26:2b769ed8de4f 557 }
Vanger 26:2b769ed8de4f 558
Vanger 32:7d5581159bed 559 //Turns off echo when it receives a true, turns on when it receives false
Vanger 26:2b769ed8de4f 560 Code EasyIP::echo(bool state)
Vanger 26:2b769ed8de4f 561 {
Vanger 27:ec44d5a9544f 562 Code code;
Vanger 27:ec44d5a9544f 563 if (state) {
Vanger 27:ec44d5a9544f 564 code = sendBasicCommand("ATE0", 1000);
Vanger 27:ec44d5a9544f 565 echoMode = (code == MTS_SUCCESS) ? false : echoMode;
Vanger 27:ec44d5a9544f 566 } else {
Vanger 27:ec44d5a9544f 567 code = sendBasicCommand("ATE1", 1000);
Vanger 27:ec44d5a9544f 568 echoMode = (code == MTS_SUCCESS) ? true : echoMode;
Vanger 27:ec44d5a9544f 569 }
Vanger 27:ec44d5a9544f 570 return code;
Vanger 26:2b769ed8de4f 571 }
Vanger 26:2b769ed8de4f 572
Vanger 26:2b769ed8de4f 573 bool EasyIP::ping(const std::string& address)
Vanger 26:2b769ed8de4f 574 {
Vanger 26:2b769ed8de4f 575 char buffer[256] = {0};
Vanger 27:ec44d5a9544f 576 std::vector<std::string> parts;
Vanger 27:ec44d5a9544f 577 int TTL=0;
Vanger 27:ec44d5a9544f 578 int Timeout=0;
Vanger 27:ec44d5a9544f 579
Vanger 27:ec44d5a9544f 580 //Format parameters for sending to radio
Vanger 30:1326b623919a 581 sprintf(buffer, "AT#PING=%s,1,32,%d", address.c_str(), (PINGDELAY*10));
Vanger 26:2b769ed8de4f 582
Vanger 27:ec44d5a9544f 583 for(int pngs=0; pngs<PINGNUM; pngs++) {
Vanger 35:257eb41405e1 584 std::string response = sendCommand(buffer, (PINGDELAY*2000)); //Send 1 ping
Vanger 52:2cb58398a4f9 585 if(response.empty() || response.find("ERROR") != std::string::npos) {
Vanger 28:f93d7b3f7c2e 586 continue; //Skip current loop if send command fails
Vanger 28:f93d7b3f7c2e 587 }
Vanger 27:ec44d5a9544f 588 parts = Text::split(response, "\r\n");
Vanger 28:f93d7b3f7c2e 589 if(parts.size() < 2) {
Vanger 28:f93d7b3f7c2e 590 continue;
Vanger 28:f93d7b3f7c2e 591 }
Vanger 27:ec44d5a9544f 592 parts = Text::split(parts[1], ",");
Vanger 28:f93d7b3f7c2e 593 if(parts.size() < 4) {
Vanger 28:f93d7b3f7c2e 594 continue;
Vanger 28:f93d7b3f7c2e 595 }
Vanger 27:ec44d5a9544f 596 //Parse TTL and Timeout values
Vanger 27:ec44d5a9544f 597 Timeout = std::atoi(parts[2].c_str());
Vanger 27:ec44d5a9544f 598 TTL = std::atoi(parts[3].c_str());
Vanger 27:ec44d5a9544f 599
Vanger 27:ec44d5a9544f 600 if((Timeout < 600) && (TTL < 255)) {
Vanger 27:ec44d5a9544f 601 return true;
Vanger 27:ec44d5a9544f 602 }
Vanger 52:2cb58398a4f9 603 }
Vanger 26:2b769ed8de4f 604 return false;
Vanger 26:2b769ed8de4f 605 }
Vanger 26:2b769ed8de4f 606
Vanger 27:ec44d5a9544f 607 //Pass 1 to enable socket closeable
Vanger 27:ec44d5a9544f 608 //Pass 0 to disable socket closeable
Vanger 26:2b769ed8de4f 609 Code EasyIP::setSocketCloseable(bool enabled)
Vanger 26:2b769ed8de4f 610 {
Vanger 31:529db15abda7 611 if(socketCloseable == enabled) {
Vanger 31:529db15abda7 612 return MTS_SUCCESS;
Vanger 31:529db15abda7 613 }
Vanger 31:529db15abda7 614
Vanger 31:529db15abda7 615 if(socketOpened) {
Vanger 31:529db15abda7 616 logError("socket is already opened. Can not set closeable");
Vanger 31:529db15abda7 617 return MTS_ERROR;
Vanger 31:529db15abda7 618 }
Vanger 31:529db15abda7 619
Vanger 31:529db15abda7 620 socketCloseable = enabled;
Vanger 31:529db15abda7 621
Vanger 26:2b769ed8de4f 622 return MTS_SUCCESS;
Vanger 26:2b769ed8de4f 623 }
Vanger 33:3b6f3904dde0 624
Vanger 33:3b6f3904dde0 625 bool EasyIP::sendEscapeCommand()
Vanger 33:3b6f3904dde0 626 {
Vanger 33:3b6f3904dde0 627 //string Cellular::sendCommand(const std::string& command, unsigned int timeoutMillis, char esc)
Vanger 33:3b6f3904dde0 628 if(io == NULL) {
Vanger 33:3b6f3904dde0 629 logError("MTSBufferedIO not set");
Vanger 33:3b6f3904dde0 630 return false;
Vanger 33:3b6f3904dde0 631 }
Vanger 33:3b6f3904dde0 632 if(!socketOpened) {
Vanger 52:2cb58398a4f9 633 logError("Socket is not open.");
Vanger 52:2cb58398a4f9 634 return false;
Vanger 33:3b6f3904dde0 635 }
Vanger 33:3b6f3904dde0 636
Vanger 33:3b6f3904dde0 637 if(!socketCloseable) {
Vanger 33:3b6f3904dde0 638 logError("Socket is not closeable");
Vanger 33:3b6f3904dde0 639 return false;
Vanger 33:3b6f3904dde0 640 }
Vanger 33:3b6f3904dde0 641
Vanger 33:3b6f3904dde0 642 io->rxClear();
Vanger 33:3b6f3904dde0 643 io->txClear();
Vanger 33:3b6f3904dde0 644
Vanger 33:3b6f3904dde0 645 std::string result;
Vanger 52:2cb58398a4f9 646 unsigned int timeoutMillis = 10000;
Vanger 33:3b6f3904dde0 647 //Attempt to write command
Vanger 52:2cb58398a4f9 648 //Format for +++ command is 1 second wait, send +++, then another second wait
Vanger 52:2cb58398a4f9 649 wait(1.2);
Vanger 49:1fc51c53cebf 650 if(io->write("+++", 3, timeoutMillis) != 3) {
Vanger 33:3b6f3904dde0 651 //Failed to write command
Vanger 33:3b6f3904dde0 652 logError("failed to send command to radio within %d milliseconds", timeoutMillis);
Vanger 33:3b6f3904dde0 653 return false;
Vanger 33:3b6f3904dde0 654 }
Vanger 33:3b6f3904dde0 655
Vanger 52:2cb58398a4f9 656 Timer tmr;
Vanger 33:3b6f3904dde0 657 char tmp[256];
Vanger 33:3b6f3904dde0 658 tmp[255] = 0;
Vanger 33:3b6f3904dde0 659 bool done = false;
Vanger 33:3b6f3904dde0 660 bool exitmode = false;
Vanger 52:2cb58398a4f9 661 tmr.start();
Vanger 33:3b6f3904dde0 662 do {
Vanger 33:3b6f3904dde0 663 //Make a non-blocking read call by passing timeout of zero
Vanger 52:2cb58398a4f9 664 int size = io->read(tmp,255,0); //1 less than allocated (timeout is instant)
Vanger 52:2cb58398a4f9 665 if(size > 0) {
Vanger 52:2cb58398a4f9 666 result.append(tmp, size);
Vanger 52:2cb58398a4f9 667 }
Vanger 54:a6c738bfc391 668 if(result.find("OK\r\n") != std::string::npos) {
Vanger 33:3b6f3904dde0 669 exitmode = true;
Vanger 33:3b6f3904dde0 670 done = true;
Vanger 54:a6c738bfc391 671 } else if(result.find("NO CARRIER\r\n") != std::string::npos) {
Vanger 52:2cb58398a4f9 672 socketOpened = false;
Vanger 33:3b6f3904dde0 673 exitmode = true;
Vanger 33:3b6f3904dde0 674 done = true;
Vanger 33:3b6f3904dde0 675 } else if(result.find("ERROR") != std::string::npos) {
Vanger 33:3b6f3904dde0 676 exitmode = false;
Vanger 33:3b6f3904dde0 677 done = true;
Vanger 33:3b6f3904dde0 678 }
Vanger 52:2cb58398a4f9 679 if(tmr.read_ms() >= timeoutMillis) {
Vanger 33:3b6f3904dde0 680 logDebug("Escape sequence [+++] timed out after %d milliseconds", timeoutMillis);
Vanger 33:3b6f3904dde0 681 exitmode = true;
Vanger 33:3b6f3904dde0 682 done = true;
Vanger 33:3b6f3904dde0 683 }
Vanger 33:3b6f3904dde0 684 } while (!done);
Vanger 33:3b6f3904dde0 685
Vanger 54:a6c738bfc391 686 wait(0.1); //Without a slight wait time after receiving OK, radio would
Vanger 54:a6c738bfc391 687 //fail to correctly receive and respond to the next AT command
Vanger 33:3b6f3904dde0 688 return exitmode;
Vanger 33:3b6f3904dde0 689 }
Vanger 33:3b6f3904dde0 690
Vanger 33:3b6f3904dde0 691 bool EasyIP::socketCheck() {
Vanger 33:3b6f3904dde0 692 bool status = false;
Vanger 52:2cb58398a4f9 693 int socketInfo = 9; //error
Vanger 52:2cb58398a4f9 694 enum SocketStatus {SOCKETCLOSED, SOCKETACTIVEDATA, SOCKETSUSPEND, SOCKETSUSPENDDATA, SOCKETLISTEN, SOCKETINCOMING, ERROR = 9};
Vanger 33:3b6f3904dde0 695 std::vector<std::string> params;
Vanger 33:3b6f3904dde0 696
Vanger 33:3b6f3904dde0 697 //Goes from data mode to command mode
Vanger 33:3b6f3904dde0 698 if(sendEscapeCommand()) {
Vanger 52:2cb58398a4f9 699 std::string reply = sendCommand("AT#SS=1", 2000);
Vanger 52:2cb58398a4f9 700 if(reply.find("OK") != std::string::npos) {
Vanger 52:2cb58398a4f9 701 //Found valid response
Vanger 52:2cb58398a4f9 702 params = Text::split(reply, "\r\n");
Vanger 52:2cb58398a4f9 703 params = Text::split(params[1], ",");
Vanger 52:2cb58398a4f9 704 socketInfo = atoi(params[1].c_str());
Vanger 52:2cb58398a4f9 705 } else {
Vanger 52:2cb58398a4f9 706 logError("Could not determine socket status[%d]",socketInfo);
Vanger 52:2cb58398a4f9 707 socketInfo = 9; //9 is unrecognized
Vanger 33:3b6f3904dde0 708 }
Vanger 33:3b6f3904dde0 709 } else {
Vanger 33:3b6f3904dde0 710 status = false; //Return value of socketOpened when checking
Vanger 33:3b6f3904dde0 711 }
Vanger 33:3b6f3904dde0 712
Vanger 33:3b6f3904dde0 713 //Check socket status query
Vanger 52:2cb58398a4f9 714 if(socketInfo < 5 && socketInfo > 0) { //Socket opened responses
Vanger 52:2cb58398a4f9 715 status = true;
Vanger 52:2cb58398a4f9 716 } else if(socketInfo == SOCKETCLOSED || socketInfo == SOCKETINCOMING) {
Vanger 52:2cb58398a4f9 717 status = false;
Vanger 33:3b6f3904dde0 718 } else {
Vanger 54:a6c738bfc391 719 logError("Could not determine socket status[%d]",socketInfo);
Vanger 52:2cb58398a4f9 720 status = false;
Vanger 33:3b6f3904dde0 721 }
Vanger 33:3b6f3904dde0 722
Vanger 52:2cb58398a4f9 723 //Reconnect to active socket if able
Vanger 33:3b6f3904dde0 724 if(status) {
Vanger 33:3b6f3904dde0 725 std::string reconnect = sendCommand("AT#SO=1", 2000);
Vanger 33:3b6f3904dde0 726 if(reconnect.find("CONNECT") != std::string::npos || reconnect.find("OK") != std::string::npos) {
Vanger 33:3b6f3904dde0 727 } else {
Vanger 33:3b6f3904dde0 728 logError("Failed to resume socket connection");
Vanger 33:3b6f3904dde0 729 }
Vanger 33:3b6f3904dde0 730 }
Vanger 33:3b6f3904dde0 731 return status;
Vanger 33:3b6f3904dde0 732 }