INSAT Iot LAB Mustapha HAMDI

Dependencies:   Moisture_Quickstart_IBM_Cloud NetworkSocketAPI X_NUCLEO_IDW01M1v2 mbed

Committer:
Hamdi
Date:
Sat Feb 24 10:18:38 2018 +0000
Revision:
0:2d9e461a3fd4
INSAT IoT LAB

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Hamdi 0:2d9e461a3fd4 1 /* SpwfInterface NetworkSocketAPI Example Program
Hamdi 0:2d9e461a3fd4 2 * Copyright (c) 2015 ARM Limited
Hamdi 0:2d9e461a3fd4 3 *
Hamdi 0:2d9e461a3fd4 4 * Licensed under the Apache License, Version 2.0 (the "License");
Hamdi 0:2d9e461a3fd4 5 * you may not use this file except in compliance with the License.
Hamdi 0:2d9e461a3fd4 6 * You may obtain a copy of the License at
Hamdi 0:2d9e461a3fd4 7 *
Hamdi 0:2d9e461a3fd4 8 * http://www.apache.org/licenses/LICENSE-2.0
Hamdi 0:2d9e461a3fd4 9 *
Hamdi 0:2d9e461a3fd4 10 * Unless required by applicable law or agreed to in writing, software
Hamdi 0:2d9e461a3fd4 11 * distributed under the License is distributed on an "AS IS" BASIS,
Hamdi 0:2d9e461a3fd4 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Hamdi 0:2d9e461a3fd4 13 * See the License for the specific language governing permissions and
Hamdi 0:2d9e461a3fd4 14 * limitations under the License.
Hamdi 0:2d9e461a3fd4 15 */
Hamdi 0:2d9e461a3fd4 16
Hamdi 0:2d9e461a3fd4 17 #include "mbed.h"
Hamdi 0:2d9e461a3fd4 18 #include "SpwfInterface.h"
Hamdi 0:2d9e461a3fd4 19 #include "TCPSocket.h"
Hamdi 0:2d9e461a3fd4 20 #include "MQTTClient.h"
Hamdi 0:2d9e461a3fd4 21 #include "MQTTWiFi.h"
Hamdi 0:2d9e461a3fd4 22 #include <ctype.h>
Hamdi 0:2d9e461a3fd4 23
Hamdi 0:2d9e461a3fd4 24
Hamdi 0:2d9e461a3fd4 25 //------------------------------------
Hamdi 0:2d9e461a3fd4 26 // Hyperterminal configuration
Hamdi 0:2d9e461a3fd4 27 // 9600 bauds, 8-bit data, no parity
Hamdi 0:2d9e461a3fd4 28 //------------------------------------
Hamdi 0:2d9e461a3fd4 29 Serial pc(SERIAL_TX, SERIAL_RX);
Hamdi 0:2d9e461a3fd4 30 DigitalOut myled(LED1);
Hamdi 0:2d9e461a3fd4 31 bool quickstartMode = true;
Hamdi 0:2d9e461a3fd4 32
Hamdi 0:2d9e461a3fd4 33 //DigitalOut heater(D12);
Hamdi 0:2d9e461a3fd4 34 AnalogIn sensorG(D13);
Hamdi 0:2d9e461a3fd4 35
Hamdi 0:2d9e461a3fd4 36
Hamdi 0:2d9e461a3fd4 37
Hamdi 0:2d9e461a3fd4 38 //DigitalIn sensor(D13);
Hamdi 0:2d9e461a3fd4 39 float value = 0;
Hamdi 0:2d9e461a3fd4 40
Hamdi 0:2d9e461a3fd4 41 #define ORG_QUICKSTART // comment to connect to play.internetofthings.ibmcloud.co
Hamdi 0:2d9e461a3fd4 42 //#define SUBSCRIBE // uncomment to subscribe to broker msgs (not to be used with IBM broker)
Hamdi 0:2d9e461a3fd4 43 #define X_NUCLEO_NFC01A1_PRESENT // uncomment to add NFC support
Hamdi 0:2d9e461a3fd4 44
Hamdi 0:2d9e461a3fd4 45 #define MQTT_MAX_PACKET_SIZE 250
Hamdi 0:2d9e461a3fd4 46 #define MQTT_MAX_PAYLOAD_SIZE 300
Hamdi 0:2d9e461a3fd4 47
Hamdi 0:2d9e461a3fd4 48 // Configuration values needed to connect to IBM IoT Cloud
Hamdi 0:2d9e461a3fd4 49 #define BROKER_URL ".messaging.internetofthings.ibmcloud.com";
Hamdi 0:2d9e461a3fd4 50 #ifdef ORG_QUICKSTART
Hamdi 0:2d9e461a3fd4 51 #define ORG "quickstart" // connect to quickstart.internetofthings.ibmcloud.com/ For a registered connection, replace with your org
Hamdi 0:2d9e461a3fd4 52 #define ID ""
Hamdi 0:2d9e461a3fd4 53 #define AUTH_TOKEN ""
Hamdi 0:2d9e461a3fd4 54 #define DEFAULT_TYPE_NAME "iotsample-mbed-Nucleo"
Hamdi 0:2d9e461a3fd4 55 #else // not def ORG_QUICKSTART
Hamdi 0:2d9e461a3fd4 56 #define ORG "" // connect to play.internetofthings.ibmcloud.com/ For a registered connection, replace with your org
Hamdi 0:2d9e461a3fd4 57 #define ID "" // For a registered connection, replace with your id 0080E1B8D765
Hamdi 0:2d9e461a3fd4 58 #define AUTH_TOKEN ""// For a registered connection, replace with your auth-token
Hamdi 0:2d9e461a3fd4 59 #define DEFAULT_TYPE_NAME ""
Hamdi 0:2d9e461a3fd4 60 #endif
Hamdi 0:2d9e461a3fd4 61 #define TOPIC "iot-2/evt/status/fmt/json"
Hamdi 0:2d9e461a3fd4 62
Hamdi 0:2d9e461a3fd4 63 #define TYPE DEFAULT_TYPE_NAME // For a registered connection, replace with your type
Hamdi 0:2d9e461a3fd4 64 #define MQTT_PORT 1883
Hamdi 0:2d9e461a3fd4 65 #define MQTT_TLS_PORT 8883
Hamdi 0:2d9e461a3fd4 66 #define IBM_IOT_PORT MQTT_PORT
Hamdi 0:2d9e461a3fd4 67 // WiFi network credential
Hamdi 0:2d9e461a3fd4 68 #define SSID "STM" // Network must be visible otherwise it can't connect
Hamdi 0:2d9e461a3fd4 69 #define PASSW "STMdemoPWD"
Hamdi 0:2d9e461a3fd4 70 #warning "Wifi SSID & password empty"
Hamdi 0:2d9e461a3fd4 71
Hamdi 0:2d9e461a3fd4 72 char id[30] = ID; // mac without colons
Hamdi 0:2d9e461a3fd4 73 char org[12] = ORG;
Hamdi 0:2d9e461a3fd4 74 int connack_rc = 0; // MQTT connack return code
Hamdi 0:2d9e461a3fd4 75 const char* ip_addr = "";
Hamdi 0:2d9e461a3fd4 76 char* host_addr = "";
Hamdi 0:2d9e461a3fd4 77 char type[30] = TYPE;
Hamdi 0:2d9e461a3fd4 78 char auth_token[30] = AUTH_TOKEN; // Auth_token is only used in non-quickstart mode
Hamdi 0:2d9e461a3fd4 79 bool netConnecting = false;
Hamdi 0:2d9e461a3fd4 80 int connectTimeout = 1000;
Hamdi 0:2d9e461a3fd4 81 bool mqttConnecting = false;
Hamdi 0:2d9e461a3fd4 82 bool netConnected = false;
Hamdi 0:2d9e461a3fd4 83 bool connected = false;
Hamdi 0:2d9e461a3fd4 84 int retryAttempt = 0;
Hamdi 0:2d9e461a3fd4 85 char subscription_url[MQTT_MAX_PAYLOAD_SIZE];
Hamdi 0:2d9e461a3fd4 86
Hamdi 0:2d9e461a3fd4 87
Hamdi 0:2d9e461a3fd4 88
Hamdi 0:2d9e461a3fd4 89 MQTT::Message message;
Hamdi 0:2d9e461a3fd4 90 MQTTString TopicName={TOPIC};
Hamdi 0:2d9e461a3fd4 91 MQTT::MessageData MsgData(TopicName, message);
Hamdi 0:2d9e461a3fd4 92
Hamdi 0:2d9e461a3fd4 93 void subscribe_cb(MQTT::MessageData & msgMQTT) {
Hamdi 0:2d9e461a3fd4 94 char msg[MQTT_MAX_PAYLOAD_SIZE];
Hamdi 0:2d9e461a3fd4 95 msg[0]='\0';
Hamdi 0:2d9e461a3fd4 96 strncat (msg, (char*)msgMQTT.message.payload, msgMQTT.message.payloadlen);
Hamdi 0:2d9e461a3fd4 97 printf ("--->>> subscribe_cb msg: %s\n\r", msg);
Hamdi 0:2d9e461a3fd4 98 }
Hamdi 0:2d9e461a3fd4 99
Hamdi 0:2d9e461a3fd4 100 int subscribe(MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTWiFi* ipstack)
Hamdi 0:2d9e461a3fd4 101 {
Hamdi 0:2d9e461a3fd4 102 char* pubTopic = TOPIC;
Hamdi 0:2d9e461a3fd4 103 return client->subscribe(pubTopic, MQTT::QOS1, subscribe_cb);
Hamdi 0:2d9e461a3fd4 104 }
Hamdi 0:2d9e461a3fd4 105
Hamdi 0:2d9e461a3fd4 106 int connect(MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTWiFi* ipstack)
Hamdi 0:2d9e461a3fd4 107 {
Hamdi 0:2d9e461a3fd4 108 const char* iot_ibm = BROKER_URL;
Hamdi 0:2d9e461a3fd4 109
Hamdi 0:2d9e461a3fd4 110
Hamdi 0:2d9e461a3fd4 111 char hostname[strlen(org) + strlen(iot_ibm) + 1];
Hamdi 0:2d9e461a3fd4 112 sprintf(hostname, "%s%s", org, iot_ibm);
Hamdi 0:2d9e461a3fd4 113 SpwfSAInterface& WiFi = ipstack->getWiFi();
Hamdi 0:2d9e461a3fd4 114 // ip_addr = WiFi.get_ip_address();
Hamdi 0:2d9e461a3fd4 115 // Construct clientId - d:org:type:id
Hamdi 0:2d9e461a3fd4 116 char clientId[strlen(org) + strlen(type) + strlen(id) + 5];
Hamdi 0:2d9e461a3fd4 117 sprintf(clientId, "d:%s:%s:%s", org, type, id);
Hamdi 0:2d9e461a3fd4 118 sprintf(subscription_url, "%s.%s/#/device/%s/iotsample-Insat2/", org, "internetofthings.ibmcloud.com",id);
Hamdi 0:2d9e461a3fd4 119
Hamdi 0:2d9e461a3fd4 120 // Network debug statements
Hamdi 0:2d9e461a3fd4 121 LOG("=====================================\n\r");
Hamdi 0:2d9e461a3fd4 122 LOG("Connecting WiFi.\n\r");
Hamdi 0:2d9e461a3fd4 123 LOG("Nucleo IP ADDRESS: %s\n\r", WiFi.get_ip_address());
Hamdi 0:2d9e461a3fd4 124 LOG("Nucleo MAC ADDRESS: %s\n\r", WiFi.get_mac_address());
Hamdi 0:2d9e461a3fd4 125 LOG("Server Hostname: %s port: %d\n\r", hostname, IBM_IOT_PORT);
Hamdi 0:2d9e461a3fd4 126 // for(int i = 0; clientId[i]; i++){ // set lowercase mac
Hamdi 0:2d9e461a3fd4 127 // clientId[i] = tolower(clientId[i]);
Hamdi 0:2d9e461a3fd4 128 // }
Hamdi 0:2d9e461a3fd4 129 LOG("Client ID: %s\n\r", clientId);
Hamdi 0:2d9e461a3fd4 130 LOG("Topic: %s\n\r",TOPIC);
Hamdi 0:2d9e461a3fd4 131 LOG("Subscription URL: %s\n\r", subscription_url);
Hamdi 0:2d9e461a3fd4 132 LOG("=====================================\n\r");
Hamdi 0:2d9e461a3fd4 133
Hamdi 0:2d9e461a3fd4 134 netConnecting = true;
Hamdi 0:2d9e461a3fd4 135 ipstack->open(&ipstack->getWiFi());
Hamdi 0:2d9e461a3fd4 136 int rc = ipstack->connect(hostname, IBM_IOT_PORT, connectTimeout);
Hamdi 0:2d9e461a3fd4 137 if (rc != 0)
Hamdi 0:2d9e461a3fd4 138 {
Hamdi 0:2d9e461a3fd4 139 WARN("IP Stack connect returned: %d\n", rc);
Hamdi 0:2d9e461a3fd4 140 return rc;
Hamdi 0:2d9e461a3fd4 141 }
Hamdi 0:2d9e461a3fd4 142 printf ("--->TCP Connected\n\r");
Hamdi 0:2d9e461a3fd4 143 netConnected = true;
Hamdi 0:2d9e461a3fd4 144 netConnecting = false;
Hamdi 0:2d9e461a3fd4 145
Hamdi 0:2d9e461a3fd4 146 // MQTT Connect
Hamdi 0:2d9e461a3fd4 147 mqttConnecting = true;
Hamdi 0:2d9e461a3fd4 148 MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
Hamdi 0:2d9e461a3fd4 149 data.MQTTVersion = 4;
Hamdi 0:2d9e461a3fd4 150 data.struct_version=0;
Hamdi 0:2d9e461a3fd4 151 data.clientID.cstring = clientId;
Hamdi 0:2d9e461a3fd4 152
Hamdi 0:2d9e461a3fd4 153 if (!quickstartMode)
Hamdi 0:2d9e461a3fd4 154 {
Hamdi 0:2d9e461a3fd4 155 data.username.cstring = "use-token-auth";
Hamdi 0:2d9e461a3fd4 156 data.password.cstring = auth_token;
Hamdi 0:2d9e461a3fd4 157 }
Hamdi 0:2d9e461a3fd4 158 if ((rc = client->connect(data)) == 0)
Hamdi 0:2d9e461a3fd4 159 {
Hamdi 0:2d9e461a3fd4 160 connected = true;
Hamdi 0:2d9e461a3fd4 161 printf ("--->MQTT Connected\n\r");
Hamdi 0:2d9e461a3fd4 162 #ifdef SUBSCRIBE
Hamdi 0:2d9e461a3fd4 163 if (!subscribe(client, ipstack)) printf ("--->>>MQTT subscribed to: %s\n\r",TOPIC);
Hamdi 0:2d9e461a3fd4 164 #endif
Hamdi 0:2d9e461a3fd4 165 }
Hamdi 0:2d9e461a3fd4 166 else {
Hamdi 0:2d9e461a3fd4 167 WARN("MQTT connect returned %d\n", rc);
Hamdi 0:2d9e461a3fd4 168 }
Hamdi 0:2d9e461a3fd4 169 if (rc >= 0)
Hamdi 0:2d9e461a3fd4 170 connack_rc = rc;
Hamdi 0:2d9e461a3fd4 171 mqttConnecting = false;
Hamdi 0:2d9e461a3fd4 172 return rc;
Hamdi 0:2d9e461a3fd4 173 }
Hamdi 0:2d9e461a3fd4 174
Hamdi 0:2d9e461a3fd4 175 int getConnTimeout(int attemptNumber)
Hamdi 0:2d9e461a3fd4 176 { // First 10 attempts try within 3 seconds, next 10 attempts retry after every 1 minute
Hamdi 0:2d9e461a3fd4 177 // after 20 attempts, retry every 10 minutes
Hamdi 0:2d9e461a3fd4 178 return (attemptNumber < 10) ? 3 : (attemptNumber < 20) ? 60 : 600;
Hamdi 0:2d9e461a3fd4 179 }
Hamdi 0:2d9e461a3fd4 180
Hamdi 0:2d9e461a3fd4 181 void attemptConnect(MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTWiFi* ipstack)
Hamdi 0:2d9e461a3fd4 182 {
Hamdi 0:2d9e461a3fd4 183 connected = false;
Hamdi 0:2d9e461a3fd4 184
Hamdi 0:2d9e461a3fd4 185 while (connect(client, ipstack) != MQTT_CONNECTION_ACCEPTED)
Hamdi 0:2d9e461a3fd4 186 {
Hamdi 0:2d9e461a3fd4 187 if (connack_rc == MQTT_NOT_AUTHORIZED || connack_rc == MQTT_BAD_USERNAME_OR_PASSWORD) {
Hamdi 0:2d9e461a3fd4 188 printf ("File: %s, Line: %d Error: %d\n\r",__FILE__,__LINE__, connack_rc);
Hamdi 0:2d9e461a3fd4 189 return; // don't reattempt to connect if credentials are wrong
Hamdi 0:2d9e461a3fd4 190 }
Hamdi 0:2d9e461a3fd4 191 int timeout = getConnTimeout(++retryAttempt);
Hamdi 0:2d9e461a3fd4 192 WARN("Retry attempt number %d waiting %d\n", retryAttempt, timeout);
Hamdi 0:2d9e461a3fd4 193
Hamdi 0:2d9e461a3fd4 194 // if ipstack and client were on the heap we could deconstruct and goto a label where they are constructed
Hamdi 0:2d9e461a3fd4 195 // or maybe just add the proper members to do this disconnect and call attemptConnect(...)
Hamdi 0:2d9e461a3fd4 196 // this works - reset the system when the retry count gets to a threshold
Hamdi 0:2d9e461a3fd4 197 if (retryAttempt == 5)
Hamdi 0:2d9e461a3fd4 198 NVIC_SystemReset();
Hamdi 0:2d9e461a3fd4 199 else
Hamdi 0:2d9e461a3fd4 200 wait(timeout);
Hamdi 0:2d9e461a3fd4 201 }
Hamdi 0:2d9e461a3fd4 202 }
Hamdi 0:2d9e461a3fd4 203
Hamdi 0:2d9e461a3fd4 204 int publish(MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTWiFi* ipstack)
Hamdi 0:2d9e461a3fd4 205 {
Hamdi 0:2d9e461a3fd4 206 MQTT::Message message;
Hamdi 0:2d9e461a3fd4 207 char* pubTopic = TOPIC;
Hamdi 0:2d9e461a3fd4 208
Hamdi 0:2d9e461a3fd4 209 char buf[MQTT_MAX_PAYLOAD_SIZE];
Hamdi 0:2d9e461a3fd4 210
Hamdi 0:2d9e461a3fd4 211 // To Do
Hamdi 0:2d9e461a3fd4 212
Hamdi 0:2d9e461a3fd4 213 value = sensorG;
Hamdi 0:2d9e461a3fd4 214
Hamdi 0:2d9e461a3fd4 215 sprintf(buf,
Hamdi 0:2d9e461a3fd4 216 "{\"d\":{\"ST\":\"Nucleo-IoT-mbed\",\"Sensor\":%1.0f}}",
Hamdi 0:2d9e461a3fd4 217 value);
Hamdi 0:2d9e461a3fd4 218 message.qos = MQTT::QOS0;
Hamdi 0:2d9e461a3fd4 219 message.retained = false;
Hamdi 0:2d9e461a3fd4 220 message.dup = false;
Hamdi 0:2d9e461a3fd4 221 message.payload = (void*)buf;
Hamdi 0:2d9e461a3fd4 222 message.payloadlen = strlen(buf);
Hamdi 0:2d9e461a3fd4 223
Hamdi 0:2d9e461a3fd4 224 // LOG("Publishing %s\n\r", buf);
Hamdi 0:2d9e461a3fd4 225 printf("Publishing %s\n\r", buf);
Hamdi 0:2d9e461a3fd4 226 return client->publish(pubTopic, message);
Hamdi 0:2d9e461a3fd4 227 }
Hamdi 0:2d9e461a3fd4 228
Hamdi 0:2d9e461a3fd4 229 int main()
Hamdi 0:2d9e461a3fd4 230 {
Hamdi 0:2d9e461a3fd4 231 const char * ssid = SSID; // Network must be visible otherwise it can't connect
Hamdi 0:2d9e461a3fd4 232 const char * seckey = PASSW;
Hamdi 0:2d9e461a3fd4 233 SpwfSAInterface spwf(D8, D2, false);
Hamdi 0:2d9e461a3fd4 234
Hamdi 0:2d9e461a3fd4 235
Hamdi 0:2d9e461a3fd4 236 pc.printf("\r\nX-NUCLEO-IDW01M1 mbed Application\r\n");
Hamdi 0:2d9e461a3fd4 237 pc.printf("\r\nconnecting to AP\r\n");
Hamdi 0:2d9e461a3fd4 238
Hamdi 0:2d9e461a3fd4 239 quickstartMode=false;
Hamdi 0:2d9e461a3fd4 240 if (strcmp(org, "quickstart") == 0){quickstartMode = true;}
Hamdi 0:2d9e461a3fd4 241 MQTTWiFi ipstack(spwf, ssid, seckey, NSAPI_SECURITY_WPA2);
Hamdi 0:2d9e461a3fd4 242 MQTT::Client<MQTTWiFi, Countdown, MQTT_MAX_PACKET_SIZE> client(ipstack);
Hamdi 0:2d9e461a3fd4 243 if (quickstartMode){
Hamdi 0:2d9e461a3fd4 244 char mac[50]; // remove all : from mac
Hamdi 0:2d9e461a3fd4 245 char *digit=NULL;
Hamdi 0:2d9e461a3fd4 246 sprintf (id,"%s", "");
Hamdi 0:2d9e461a3fd4 247 sprintf (mac,"%s",ipstack.getWiFi().get_mac_address());
Hamdi 0:2d9e461a3fd4 248 digit = strtok (mac,":");
Hamdi 0:2d9e461a3fd4 249 while (digit != NULL)
Hamdi 0:2d9e461a3fd4 250 {
Hamdi 0:2d9e461a3fd4 251 strcat (id, digit);
Hamdi 0:2d9e461a3fd4 252 digit = strtok (NULL, ":");
Hamdi 0:2d9e461a3fd4 253 }
Hamdi 0:2d9e461a3fd4 254 }
Hamdi 0:2d9e461a3fd4 255 attemptConnect(&client, &ipstack);
Hamdi 0:2d9e461a3fd4 256 if (connack_rc == MQTT_NOT_AUTHORIZED || connack_rc == MQTT_BAD_USERNAME_OR_PASSWORD)
Hamdi 0:2d9e461a3fd4 257 {
Hamdi 0:2d9e461a3fd4 258 while (true)
Hamdi 0:2d9e461a3fd4 259 wait(1.0); // Permanent failures - don't retry
Hamdi 0:2d9e461a3fd4 260 }
Hamdi 0:2d9e461a3fd4 261
Hamdi 0:2d9e461a3fd4 262 myled=1;
Hamdi 0:2d9e461a3fd4 263 int count = 0;
Hamdi 0:2d9e461a3fd4 264 // tyeld.start();
Hamdi 0:2d9e461a3fd4 265 while (true)
Hamdi 0:2d9e461a3fd4 266 {
Hamdi 0:2d9e461a3fd4 267 if (++count == 100)
Hamdi 0:2d9e461a3fd4 268 { // Publish a message every second
Hamdi 0:2d9e461a3fd4 269 if (publish(&client, &ipstack) != 0) {
Hamdi 0:2d9e461a3fd4 270 myled=0;
Hamdi 0:2d9e461a3fd4 271 attemptConnect(&client, &ipstack); // if we have lost the connection
Hamdi 0:2d9e461a3fd4 272 } else myled=1;
Hamdi 0:2d9e461a3fd4 273 count = 0;
Hamdi 0:2d9e461a3fd4 274 }
Hamdi 0:2d9e461a3fd4 275 // int start = tyeld.read_ms();
Hamdi 0:2d9e461a3fd4 276 client.yield(10); // allow the MQTT client to receive messages
Hamdi 0:2d9e461a3fd4 277 // printf ("tyeld: %d\n\r",tyeld.read_ms()-start);
Hamdi 0:2d9e461a3fd4 278 }
Hamdi 0:2d9e461a3fd4 279 }