Jim Flynn
/
aws-iot-device-sdk-mbed-c
Changes to enabled on-line compiler
Revision 0:082731ede69f, committed 2018-05-30
- Comitter:
- JMF
- Date:
- Wed May 30 20:59:51 2018 +0000
- Commit message:
- Initial commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/0a9f033b3c-certificate.pem.crt Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWjCCAkKgAwIBAgIVAI4WZvFpWrl0tDaZGLWen1s14y1JMA0GCSqGSIb3DQEB +CwUAME0xSzBJBgNVBAsMQkFtYXpvbiBXZWIgU2VydmljZXMgTz1BbWF6b24uY29t +IEluYy4gTD1TZWF0dGxlIFNUPVdhc2hpbmd0b24gQz1VUzAeFw0xODA0MjYyMDI1 +MzlaFw00OTEyMzEyMzU5NTlaMB4xHDAaBgNVBAMME0FXUyBJb1QgQ2VydGlmaWNh +dGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNIGt67lH48pFpEG6G +DHM479Bg2mOFiWEWnGiDZaZ8hLU+RKwRDS8CYSFAqj+RxLhOha4SRMjlM8f7Y4wV +xnLOIm/N+lriw58r7ljWaAGSmJuYiJogAmu6ggeDgFzLGg7Lleb1EXpxaUshw6Xz +tQIdN7uM0J7I8Qv/YhqugMCYk0lPUvokB4HQNEeo6D0YfF7qq0EQJDTyRNs1Oacw +6QcBpA/m5uEr1RYsltL3ox5nr6eEC7A85kKpco/C5OzcyWk1cfr5u7OtzrrsP61y +8UyVOiNzdcIkYIVTvoVUG4Tb2pxtl5Se/yBRsns5eTa4BIgsCY1Fou2dfwENX2KI +z51jAgMBAAGjYDBeMB8GA1UdIwQYMBaAFCOrupg7KgH+kpd3MGlv8G3he8jhMB0G +A1UdDgQWBBQbel4Yb0L8LI0b9BzKtsOjQfvXEDAMBgNVHRMBAf8EAjAAMA4GA1Ud +DwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAQEAZK8X+A5jThXoUkuNxbO2oma/ +9f0L99L44b7Q/hymvNyh6M0JmZj7ncKn4Z+vPaqxAZ2KQ/ppYm2vljKtZkwShTes +cv/BBfHJ9hi4Y+e88JYivyRkF9dgf+WSjJ7KUV/T4tUtxNPd86uoFVPTMAzQWJzG +WAE3gEzGbMlb8fEQpmCStw9oJO1IQrlhiVNlXBedJEtyCbHKheBfcVcTusiy0CpS +2HUuC0YnzPtTUlzpXMrLlqPYmBEzj0n8yOmlwRSED5fbled/Sf1wiihlWj5xANcG +9h8kbifTae+7Vgq8IcqaGEoWEcmQuzMCx4VeA3JazTs2TBu80MLTuHXCDNj0sQ== +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/0a9f033b3c-private.pem.key Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAzSBreu5R+PKRaRBuhgxzOO/QYNpjhYlhFpxog2WmfIS1PkSs +EQ0vAmEhQKo/kcS4ToWuEkTI5TPH+2OMFcZyziJvzfpa4sOfK+5Y1mgBkpibmIia +IAJruoIHg4BcyxoOy5Xm9RF6cWlLIcOl87UCHTe7jNCeyPEL/2IaroDAmJNJT1L6 +JAeB0DRHqOg9GHxe6qtBECQ08kTbNTmnMOkHAaQP5ubhK9UWLJbS96MeZ6+nhAuw +POZCqXKPwuTs3MlpNXH6+buzrc667D+tcvFMlTojc3XCJGCFU76FVBuE29qcbZeU +nv8gUbJ7OXk2uASILAmNRaLtnX8BDV9iiM+dYwIDAQABAoIBAB3id0Y1qmOmk2DH +MBL/hVnqNmWl65Hs7eQpbWCXUqYZf35O6vKhBRRHqlyNcS1/h4qJF086Djq2TJBn +dGVHwDof8kIEE3GuJmZ0FyVFidUdvq1+WVYIhEFr6ERUUzCT30vxzvAtV7VmLbgB +lQ/ocyJmmEBvtHJ6C9FtLY323BheOg5OrC4gK3fYJm57fCk2zqzlsriqfzaSP/+7 +0bZ4M3GH2qNCaEJI+hCazDIWj2GHtVELzHyri9vehobioOavJqkg9TV0klL1dGKP +cWNadG/g9LRhUdgpi4ogOLBu8JrgZhyUjvkwkXqVCmz2AJD4/OhoxngI+eCQuFvA +Xn8QBYECgYEA86LQgVGf+3pSFYfdvLmoRoSQqxe/BkTmuSPxs5ChtcQCenM0TRYI +QanYgtWuA3hvAJxZIYsF7WtyNVeGqg9l7L9xMoFz/W+gDxAqEX9ZhVodM71VbGdQ +XJ02+Z5YrI/ZDWtnNS60tUT8pHbHcMH4vE93xV5oC7T+UHIGhEj2BRECgYEA14lQ +GyxgMG4GRvjFk97o54B6C+EXAIg4hwqi91GB7jRtPhGlL3fmBh+7EvpPJ191O/WA +pWszAbZE7WsD9tX3jx1RzLHufODGjS+9/UHjSUJ5zBHEywcUkG4kzW9qqNhCT0b4 +Zsq2c8Jta3WSaoGkCZlHs+73es1MpmfZSl7G6zMCgYBN3R4IBl87zscHDIoIM8mc +5+cYVxl81m5qgxtPCQ01ezRTkBgJ/t2apr6EaegYQdgv+MffmX/Gdg1lENj/5rXr +aO+zdX3tZExiJAdd2jZxEPC+RWdUotwjDTexpTKvy7VRvZ6Zd5jXSfn4UeHBnqxw ++nDLyjU0cJ7KdPayNmMyEQKBgBZeyBOp7WnZYYloNIl+b0nnycMF369MdCF/9x4Y +NZYVlukER9K/bP5huLblh+WcQLyyZGHG2Ipq8oh5vPWXi5QtBNpr9Ua8xZ7Nv7hT +aFWoMp/9kCFKYNYEuIkh8T4JBAjaYtm4XGtzWIv4rfOj9PSSFiEUwlbajAQRbzoK +0DaFAoGAXZ0195KdQwN8sdJDehRuLZpKYQvH3+mmdEGnW1fTtoy8+i4d1bNCYnFB +LC1GWfNvDq2vvubU0i6WzMnKPE1ixSelx75/HIK8ETNQ/q4DckEI2DwlJdEJDRGb +9icfq/kbVYV7xlbSRu04+sHn9MdOyEjhOPVLWiKDuc/6LNITOkU= +-----END RSA PRIVATE KEY-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/0a9f033b3c-public.pem.key Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzSBreu5R+PKRaRBuhgxz +OO/QYNpjhYlhFpxog2WmfIS1PkSsEQ0vAmEhQKo/kcS4ToWuEkTI5TPH+2OMFcZy +ziJvzfpa4sOfK+5Y1mgBkpibmIiaIAJruoIHg4BcyxoOy5Xm9RF6cWlLIcOl87UC +HTe7jNCeyPEL/2IaroDAmJNJT1L6JAeB0DRHqOg9GHxe6qtBECQ08kTbNTmnMOkH +AaQP5ubhK9UWLJbS96MeZ6+nhAuwPOZCqXKPwuTs3MlpNXH6+buzrc667D+tcvFM +lTojc3XCJGCFU76FVBuE29qcbZeUnv8gUbJ7OXk2uASILAmNRaLtnX8BDV9iiM+d +YwIDAQAB +-----END PUBLIC KEY-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/158a19aff7-certificate.pem.crt Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWTCCAkGgAwIBAgIUSTCBzcWYw2e+I3p9UqppjJeXv28wDQYJKoZIhvcNAQEL +BQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g +SW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE4MDUxNzAwMDM0 +NVoXDTQ5MTIzMTIzNTk1OVowHjEcMBoGA1UEAwwTQVdTIElvVCBDZXJ0aWZpY2F0 +ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKaGOyLrObsT27Tde3Oe +NosAI+YInWJSr1PGMu/SRNrSGt6wxJuSX5eaDlB7RgQ1JzRzMuhIHWwyp3VcEdkm +E3jx5/ar9Ix5L4UaSCbQIzD6f9PDiInnMTdNG9FllEixj7GqGNvWmJ8c0wcw0WSo +CG/xGkm3J8tK/D9IeVijE85mCRGwOe8zS2a3Rg77fUSpouvdqYu7HQlt6zVfoFby +MG3ADND2SaQQG+qjyarozXFg+sdv/+LxEVDVaNcBCZm9ZFcGFlYYL467vN9qOFTr +wgZzlFZMw2AIEJLgYPtdC2YMFaznn/UsoKQesVIuaJYQ5hXwfbRGquHOtukxYxgL +bSECAwEAAaNgMF4wHwYDVR0jBBgwFoAUE772Vld2Lm1m/3qC+lTF0o3et88wHQYD +VR0OBBYEFDe0mW1N7xd+v6eqm4MKaOXVrosuMAwGA1UdEwEB/wQCMAAwDgYDVR0P +AQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQDGUUjn8RzkAxrR2JD8sEnSE08y +hM10PkJENM58JijntNWwbY/PUPCaC4yNdmxrG6Z3aztyXs/2R9p1OF2kpBzcSbLA +TLOZa2qbpEcobXBLCMSMlVj0d1CshrXDxEBDBlZDpnfJVdsh5gR7sbY3FybgaQ1u +iSvDRx2cufSHU+sm1l09j+a/qyIMYChyygJm2yDKd6wGd5KNyuGzR7fyj7yHmhxu +Pdk82RG6Db4xuzrNNg+q5IzDxl/oRhKg/h8mr4hEwHXaSIXz3dfqaFe5uMAmhMeL +L1kK3FoKZD+msaE2Uj1cUGrdgfSjkZC91Z5g0PM7gFB7Tt4nNE/f+fGT1OPk +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/158a19aff7-private.pem.key Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEApoY7Ius5uxPbtN17c542iwAj5gidYlKvU8Yy79JE2tIa3rDE +m5Jfl5oOUHtGBDUnNHMy6EgdbDKndVwR2SYTePHn9qv0jHkvhRpIJtAjMPp/08OI +iecxN00b0WWUSLGPsaoY29aYnxzTBzDRZKgIb/EaSbcny0r8P0h5WKMTzmYJEbA5 +7zNLZrdGDvt9RKmi692pi7sdCW3rNV+gVvIwbcAM0PZJpBAb6qPJqujNcWD6x2// +4vERUNVo1wEJmb1kVwYWVhgvjru832o4VOvCBnOUVkzDYAgQkuBg+10LZgwVrOef +9SygpB6xUi5olhDmFfB9tEaq4c626TFjGAttIQIDAQABAoIBAE5Cp2gxxF/3ZToL +HQmpQVjyJ+zj/5NF6MAmVan/1/w+4gHmcX/ruqE09aU9ShqQIGy19ybTPYCsDadK +Ldkv29brWWjER5n9GigjweT7zI4oLAsJyhGFZcvnoTs30giX6kBFAInQwMmGcmbe +uonjrpNtbD1YposUIphbJyxQ9+33pBkyw1xjkSThwdijt3Lh+YoGM4W3JkyOAs8Y +0Q2tmUmcofD/f73JIr2oiViRWRIoL6CIsJOiFCg4GSvO1+MrvQV1lCR4EOHSLlfa +hy5T42oW7J27zDZguSNUNwkDCsEb3zSD2VyYvuhxPR+/Iufnfq89FesJFTBeYzTR +DPMbFkECgYEAzxiBNe91+k7lXyjMdZnO4YIgphrLoNUVbD84Q5dmShStHfS8H7x1 +tJIYPiNiwMabG8J2w1TdQutG8T4RLki6CSGlhrj1dA6rSsEsKy10iZiHyXokkRXQ +0haP8ZNR3vj8iFPOeSYFTQEdB5uU2p5z4Xc4kQkDlvSGabsA+1r4HysCgYEAzdkT +ph4Awv4hGzWp6MJyqAu3+vbDw/yWmy3HWCw1RHFcgwcJe/ryCaqjKJPvfpuaGoZv +DcYtAk8c4nyLSa3O0ycgP5roCmmQYL8VO3421xmFeDyAruFyj3DmObPlPa4JTJSE +n2y5yGO0FdLS+599T/e40AJgd1KWu+6V/hzCXuMCgYEAmFLh1xYGpNGbDF8p4+X5 +sQzRjttuoyGX3EKz06NsbVi/7KSyKnWn7eT3R90nKbvrIoPqbsng5PPA/ng0kuKT +U1wYL/ujLnYLr7ReFHQen4Y9eggJFV4CdkR6I6nEMkjZFjzuRVWd2IHVJF9fIV4i +xde3F5aVuVap9uLBWiATGHkCgYBWniZ3ex6E7pyNr4ociF3oum2ja9y6hnn1OFEu +mAOeFjmAZSCni9P3H1dihsV7OAyjZrgqSp673wBfiVqvGTTzgv495XgIkZ0QKLfN +ffGjl3ijPIpFjFZBZ5do5nrq9mWxVLltwDl4ygOIP1tKSDVZxfp9hNKEwzrsP5vb +/swHUwKBgE1eVI8zBIvYg/2y6N49SWrl8+9/igmx7NeQoXxK9L7HAMjZGZOgsYPn +KXbwqVdYIknbfgALVvhmxfnHBTs8gcFf9MYZ06+Soc8BP9fKhmQBKEDm+Oxb1VlI +z1R9TnQuyJhHGVesGh7MS+g/2aFC4PDxZYLNZEknBVfBwHpEYWIA +-----END RSA PRIVATE KEY-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/158a19aff7-public.pem.key Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApoY7Ius5uxPbtN17c542 +iwAj5gidYlKvU8Yy79JE2tIa3rDEm5Jfl5oOUHtGBDUnNHMy6EgdbDKndVwR2SYT +ePHn9qv0jHkvhRpIJtAjMPp/08OIiecxN00b0WWUSLGPsaoY29aYnxzTBzDRZKgI +b/EaSbcny0r8P0h5WKMTzmYJEbA57zNLZrdGDvt9RKmi692pi7sdCW3rNV+gVvIw +bcAM0PZJpBAb6qPJqujNcWD6x2//4vERUNVo1wEJmb1kVwYWVhgvjru832o4VOvC +BnOUVkzDYAgQkuBg+10LZgwVrOef9SygpB6xUi5olhDmFfB9tEaq4c626TFjGAtt +IQIDAQAB +-----END PUBLIC KEY-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/README.txt Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,4 @@ +# Copy certificates for running the samples and tests provided with the SDK into this directory +# Certificates can be created and downloaded from the AWS IoT Console +# The IoT Client takes the full path of the certificates as an input parameter while initializing +# This is the default folder for the certificates only for samples and tests. A different path can be specified if required. \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/certs/rootCA.pem.crt Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/jobs_sample/Makefile Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,70 @@ +#This target is to ensure accidental execution of Makefile as a bash script will not execute commands like rm in unexpected directories and exit gracefully. +.prevent_execution: + exit 0 + +CC = gcc + +#remove @ for no make command prints +DEBUG = @ + +APP_DIR = . +APP_INCLUDE_DIRS += -I $(APP_DIR) +APP_NAME = jobs_sample +APP_SRC_FILES = $(APP_NAME).c + +#IoT client directory +IOT_CLIENT_DIR = ../../.. + +PLATFORM_DIR = $(IOT_CLIENT_DIR)/platform/linux/mbedtls +PLATFORM_COMMON_DIR = $(IOT_CLIENT_DIR)/platform/linux/common + +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/include +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/sdk_config +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/external_libs/jsmn +IOT_INCLUDE_DIRS += -I $(PLATFORM_COMMON_DIR) +IOT_INCLUDE_DIRS += -I $(PLATFORM_DIR) + +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/src/ -name '*.c') +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/external_libs/jsmn -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_DIR)/ -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_COMMON_DIR)/ -name '*.c') + +#TLS - mbedtls +MBEDTLS_DIR = $(IOT_CLIENT_DIR)/external_libs/mbedTLS +TLS_LIB_DIR = $(MBEDTLS_DIR)/library +TLS_INCLUDE_DIR = -I $(MBEDTLS_DIR)/include +EXTERNAL_LIBS += -L$(TLS_LIB_DIR) +LD_FLAG += -Wl,-rpath,$(TLS_LIB_DIR) +LD_FLAG += -ldl $(TLS_LIB_DIR)/libmbedtls.a $(TLS_LIB_DIR)/libmbedcrypto.a $(TLS_LIB_DIR)/libmbedx509.a -lpthread + +#Aggregate all include and src directories +INCLUDE_ALL_DIRS += $(IOT_INCLUDE_DIRS) +INCLUDE_ALL_DIRS += $(TLS_INCLUDE_DIR) +INCLUDE_ALL_DIRS += $(APP_INCLUDE_DIRS) + +SRC_FILES += $(APP_SRC_FILES) +SRC_FILES += $(IOT_SRC_FILES) + +# Logging level control +LOG_FLAGS += -DENABLE_IOT_DEBUG +LOG_FLAGS += -DENABLE_IOT_INFO +LOG_FLAGS += -DENABLE_IOT_WARN +LOG_FLAGS += -DENABLE_IOT_ERROR + +COMPILER_FLAGS += $(LOG_FLAGS) +#If the processor is big endian uncomment the compiler flag +#COMPILER_FLAGS += -DREVERSED + +MBED_TLS_MAKE_CMD = $(MAKE) -C $(MBEDTLS_DIR) + +PRE_MAKE_CMD = $(MBED_TLS_MAKE_CMD) +MAKE_CMD = $(CC) $(SRC_FILES) $(COMPILER_FLAGS) -o $(APP_NAME) $(LD_FLAG) $(EXTERNAL_LIBS) $(INCLUDE_ALL_DIRS) + +all: + $(PRE_MAKE_CMD) + $(DEBUG)$(MAKE_CMD) + $(POST_MAKE_CMD) + +clean: + rm -f $(APP_DIR)/$(APP_NAME) + $(MBED_TLS_MAKE_CMD) clean
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/jobs_sample/aws_iot_config.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,73 @@ +/* + * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_config.h + * @brief AWS IoT specific configuration file + */ + +#ifndef SRC_JOBS_IOT_JOB_CONFIG_H_ +#define SRC_JOBS_IOT_JOB_CONFIG_H_ + +// Get from console +// ================================================= +#define AWS_IOT_MQTT_HOST "" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow +#define AWS_IOT_MQTT_PORT 443 ///< default port for MQTT/S +#define AWS_IOT_MQTT_CLIENT_ID "c-sdk-client-id" ///< MQTT client ID should be unique for every device +#define AWS_IOT_MY_THING_NAME "AWS-IoT-C-SDK" ///< Thing Name of the Shadow this device is associated with +#define AWS_IOT_ROOT_CA_FILENAME "rootCA.crt" ///< Root CA file name +#define AWS_IOT_CERTIFICATE_FILENAME "cert.pem" ///< device signed certificate file name +#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem" ///< Device private key filename + +// MQTT PubSub +#ifndef DISABLE_IOT_JOBS +#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped. +#else +#define AWS_IOT_MQTT_RX_BUF_LEN 2048 +#endif +#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow +#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow + +// Shadow and Job common configs +#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments" +#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 ///< This is size of the extra sequence number that will be appended to the Unique client Id +#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 ///< This is size of the the total clientToken key and value pair in the JSON +#define MAX_SIZE_OF_THING_NAME 30 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger + +// Thing Shadow specific configs +#define SHADOW_MAX_SIZE_OF_RX_BUFFER 512 ///< Maximum size of the SHADOW buffer to store the received Shadow message +#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested +#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time +#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published +#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name +#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME ///< This size includes the length of topic with Thing Name + +// Job specific configs +#ifndef DISABLE_IOT_JOBS +#define MAX_SIZE_OF_JOB_ID 64 +#define MAX_JOB_JSON_TOKEN_EXPECTED 120 +#define MAX_SIZE_OF_JOB_REQUEST AWS_IOT_MQTT_TX_BUF_LEN + +#define MAX_JOB_TOPIC_LENGTH_WITHOUT_JOB_ID_OR_THING_NAME 40 +#define MAX_JOB_TOPIC_LENGTH_BYTES MAX_JOB_TOPIC_LENGTH_WITHOUT_JOB_ID_OR_THING_NAME + MAX_SIZE_OF_THING_NAME + MAX_SIZE_OF_JOB_ID + 2 +#endif + +// Auto Reconnect specific config +#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm +#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect. + +#define DISABLE_METRICS false ///< Disable the collection of metrics by setting this to true + +#endif /* SRC_JOBS_IOT_JOB_CONFIG_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/jobs_sample/jobs_sample.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,368 @@ +/* + * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * + * This example takes the parameters from the aws_iot_config.h file and establishes + * a connection to the AWS IoT MQTT Platform. It performs several operations to + * demonstrate the basic capabilities of the AWS IoT Jobs platform. + * + * If all the certs are correct, you should see the list of pending Job Executions + * printed out by the iot_get_pending_callback_handler. If there are any existing pending + * job executions each will be processed one at a time in the iot_next_job_callback_handler. + * After all of the pending jobs have been processed the program will wait for + * notifications for new pending jobs and process them one at a time as they come in. + * + * In the main body you can see how each callback is registered for each corresponding + * Jobs topic. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> +#include <limits.h> +#include <string.h> + +#include "aws_iot_config.h" +#include "aws_iot_json_utils.h" +#include "aws_iot_log.h" +#include "aws_iot_version.h" +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_jobs_interface.h" + +/** + * @brief Default cert location + */ +char certDirectory[PATH_MAX + 1] = "../../../certs"; + +/** + * @brief Default MQTT HOST URL is pulled from the aws_iot_config.h + */ +char HostAddress[255] = AWS_IOT_MQTT_HOST; + +/** + * @brief Default MQTT port is pulled from the aws_iot_config.h + */ +uint32_t port = AWS_IOT_MQTT_PORT; + +static jsmn_parser jsonParser; +static jsmntok_t jsonTokenStruct[MAX_JSON_TOKEN_EXPECTED]; +static int32_t tokenCount; + +void iot_get_pending_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) { + IOT_UNUSED(pData); + IOT_UNUSED(pClient); + IOT_INFO("\nJOB_GET_PENDING_TOPIC callback"); + IOT_INFO("topic: %.*s", topicNameLen, topicName); + IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload); + + jsmn_init(&jsonParser); + + tokenCount = jsmn_parse(&jsonParser, params->payload, (int) params->payloadLen, jsonTokenStruct, MAX_JSON_TOKEN_EXPECTED); + + if(tokenCount < 0) { + IOT_WARN("Failed to parse JSON: %d", tokenCount); + return; + } + + /* Assume the top-level element is an object */ + if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) { + IOT_WARN("Top Level is not an object"); + return; + } + + jsmntok_t *jobs; + + jobs = findToken("inProgressJobs", params->payload, jsonTokenStruct); + + if (jobs) { + IOT_INFO("inProgressJobs: %.*s", jobs->end - jobs->start, (char *)params->payload + jobs->start); + } + + jobs = findToken("queuedJobs", params->payload, jsonTokenStruct); + + if (jobs) { + IOT_INFO("queuedJobs: %.*s", jobs->end - jobs->start, (char *)params->payload + jobs->start); + } +} + +void iot_next_job_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) { + char topicToPublishUpdate[MAX_JOB_TOPIC_LENGTH_BYTES]; + char messageBuffer[200]; + + IOT_UNUSED(pData); + IOT_UNUSED(pClient); + IOT_INFO("\nJOB_NOTIFY_NEXT_TOPIC / JOB_DESCRIBE_TOPIC($next) callback"); + IOT_INFO("topic: %.*s", topicNameLen, topicName); + IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload); + + jsmn_init(&jsonParser); + + tokenCount = jsmn_parse(&jsonParser, params->payload, (int) params->payloadLen, jsonTokenStruct, MAX_JSON_TOKEN_EXPECTED); + + if(tokenCount < 0) { + IOT_WARN("Failed to parse JSON: %d", tokenCount); + return; + } + + /* Assume the top-level element is an object */ + if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) { + IOT_WARN("Top Level is not an object"); + return; + } + + jsmntok_t *tokExecution; + + tokExecution = findToken("execution", params->payload, jsonTokenStruct); + + if (tokExecution) { + IOT_INFO("execution: %.*s", tokExecution->end - tokExecution->start, (char *)params->payload + tokExecution->start); + + jsmntok_t *tok; + + tok = findToken("jobId", params->payload, tokExecution); + + if (tok) { + IoT_Error_t rc; + char jobId[MAX_SIZE_OF_JOB_ID + 1]; + AwsIotJobExecutionUpdateRequest updateRequest; + + rc = parseStringValue(jobId, MAX_SIZE_OF_JOB_ID + 1, params->payload, tok); + if(SUCCESS != rc) { + IOT_ERROR("parseStringValue returned error : %d ", rc); + return; + } + + IOT_INFO("jobId: %s", jobId); + + tok = findToken("jobDocument", params->payload, tokExecution); + + /* + * Do your job processing here. + */ + + if (tok) { + IOT_INFO("jobDocument: %.*s", tok->end - tok->start, (char *)params->payload + tok->start); + /* Alternatively if the job still has more steps the status can be set to JOB_EXECUTION_IN_PROGRESS instead */ + updateRequest.status = JOB_EXECUTION_SUCCEEDED; + updateRequest.statusDetails = "{\"exampleDetail\":\"a value appropriate for your successful job\"}"; + } else { + updateRequest.status = JOB_EXECUTION_FAILED; + updateRequest.statusDetails = "{\"failureDetail\":\"Unable to process job document\"}"; + } + + updateRequest.expectedVersion = 0; + updateRequest.executionNumber = 0; + updateRequest.includeJobExecutionState = false; + updateRequest.includeJobDocument = false; + updateRequest.clientToken = NULL; + + rc = aws_iot_jobs_send_update(pClient, QOS0, AWS_IOT_MY_THING_NAME, jobId, &updateRequest, + topicToPublishUpdate, sizeof(topicToPublishUpdate), messageBuffer, sizeof(messageBuffer)); + } + } else { + IOT_INFO("execution property not found, nothing to do"); + } +} + +void iot_update_accepted_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) { + IOT_UNUSED(pData); + IOT_UNUSED(pClient); + IOT_INFO("\nJOB_UPDATE_TOPIC / accepted callback"); + IOT_INFO("topic: %.*s", topicNameLen, topicName); + IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload); +} + +void iot_update_rejected_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) { + IOT_UNUSED(pData); + IOT_UNUSED(pClient); + IOT_INFO("\nJOB_UPDATE_TOPIC / rejected callback"); + IOT_INFO("topic: %.*s", topicNameLen, topicName); + IOT_INFO("payload: %.*s", (int) params->payloadLen, (char *)params->payload); + + /* Do error handling here for when the update was rejected */ +} + +void disconnectCallbackHandler(AWS_IoT_Client *pClient, void *data) { + IOT_WARN("MQTT Disconnect"); + IoT_Error_t rc = FAILURE; + + if(NULL == pClient) { + return; + } + + IOT_UNUSED(data); + + if(aws_iot_is_autoreconnect_enabled(pClient)) { + IOT_INFO("Auto Reconnect is enabled, Reconnecting attempt will start now"); + } else { + IOT_WARN("Auto Reconnect not enabled. Starting manual reconnect..."); + rc = aws_iot_mqtt_attempt_reconnect(pClient); + if(NETWORK_RECONNECTED == rc) { + IOT_WARN("Manual Reconnect Successful"); + } else { + IOT_WARN("Manual Reconnect Failed - %d", rc); + } + } +} + +int main(int argc, char **argv) { + char rootCA[PATH_MAX + 1]; + char clientCRT[PATH_MAX + 1]; + char clientKey[PATH_MAX + 1]; + char CurrentWD[PATH_MAX + 1]; + char cPayload[100]; + + int32_t i = 0; + + IoT_Error_t rc = FAILURE; + + AWS_IoT_Client client; + IoT_Client_Init_Params mqttInitParams = iotClientInitParamsDefault; + IoT_Client_Connect_Params connectParams = iotClientConnectParamsDefault; + + IoT_Publish_Message_Params paramsQOS0; + + getcwd(CurrentWD, sizeof(CurrentWD)); + snprintf(rootCA, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_ROOT_CA_FILENAME); + snprintf(clientCRT, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_CERTIFICATE_FILENAME); + snprintf(clientKey, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_PRIVATE_KEY_FILENAME); + + IOT_DEBUG("rootCA %s", rootCA); + IOT_DEBUG("clientCRT %s", clientCRT); + IOT_DEBUG("clientKey %s", clientKey); + + mqttInitParams.enableAutoReconnect = false; // We enable this later below + mqttInitParams.pHostURL = HostAddress; + mqttInitParams.port = port; + mqttInitParams.pRootCALocation = rootCA; + mqttInitParams.pDeviceCertLocation = clientCRT; + mqttInitParams.pDevicePrivateKeyLocation = clientKey; + mqttInitParams.mqttCommandTimeout_ms = 20000; + mqttInitParams.tlsHandshakeTimeout_ms = 5000; + mqttInitParams.isSSLHostnameVerify = true; + mqttInitParams.disconnectHandler = disconnectCallbackHandler; + mqttInitParams.disconnectHandlerData = NULL; + + rc = aws_iot_mqtt_init(&client, &mqttInitParams); + if(SUCCESS != rc) { + IOT_ERROR("aws_iot_mqtt_init returned error : %d ", rc); + return rc; + } + + connectParams.keepAliveIntervalInSec = 600; + connectParams.isCleanSession = true; + connectParams.MQTTVersion = MQTT_3_1_1; + connectParams.pClientID = AWS_IOT_MQTT_CLIENT_ID; + connectParams.clientIDLen = (uint16_t) strlen(AWS_IOT_MQTT_CLIENT_ID); + connectParams.isWillMsgPresent = false; + + IOT_INFO("Connecting..."); + rc = aws_iot_mqtt_connect(&client, &connectParams); + if(SUCCESS != rc) { + IOT_ERROR("Error(%d) connecting to %s:%d", rc, mqttInitParams.pHostURL, mqttInitParams.port); + return rc; + } + /* + * Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h + * #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL + * #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL + */ + rc = aws_iot_mqtt_autoreconnect_set_status(&client, true); + if(SUCCESS != rc) { + IOT_ERROR("Unable to set Auto Reconnect to true - %d", rc); + return rc; + } + + char topicToSubscribeGetPending[MAX_JOB_TOPIC_LENGTH_BYTES]; + char topicToSubscribeNotifyNext[MAX_JOB_TOPIC_LENGTH_BYTES]; + char topicToSubscribeGetNext[MAX_JOB_TOPIC_LENGTH_BYTES]; + char topicToSubscribeUpdateAccepted[MAX_JOB_TOPIC_LENGTH_BYTES]; + char topicToSubscribeUpdateRejected[MAX_JOB_TOPIC_LENGTH_BYTES]; + + char topicToPublishGetPending[MAX_JOB_TOPIC_LENGTH_BYTES]; + char topicToPublishGetNext[MAX_JOB_TOPIC_LENGTH_BYTES]; + + rc = aws_iot_jobs_subscribe_to_job_messages( + &client, QOS0, AWS_IOT_MY_THING_NAME, NULL, JOB_GET_PENDING_TOPIC, JOB_WILDCARD_REPLY_TYPE, + iot_get_pending_callback_handler, NULL, topicToSubscribeGetPending, sizeof(topicToSubscribeGetPending)); + + if(SUCCESS != rc) { + IOT_ERROR("Error subscribing JOB_GET_PENDING_TOPIC: %d ", rc); + return rc; + } + + rc = aws_iot_jobs_subscribe_to_job_messages( + &client, QOS0, AWS_IOT_MY_THING_NAME, NULL, JOB_NOTIFY_NEXT_TOPIC, JOB_REQUEST_TYPE, + iot_next_job_callback_handler, NULL, topicToSubscribeNotifyNext, sizeof(topicToSubscribeNotifyNext)); + + if(SUCCESS != rc) { + IOT_ERROR("Error subscribing JOB_NOTIFY_NEXT_TOPIC: %d ", rc); + return rc; + } + + rc = aws_iot_jobs_subscribe_to_job_messages( + &client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_NEXT, JOB_DESCRIBE_TOPIC, JOB_WILDCARD_REPLY_TYPE, + iot_next_job_callback_handler, NULL, topicToSubscribeGetNext, sizeof(topicToSubscribeGetNext)); + + if(SUCCESS != rc) { + IOT_ERROR("Error subscribing JOB_DESCRIBE_TOPIC ($next): %d ", rc); + return rc; + } + + rc = aws_iot_jobs_subscribe_to_job_messages( + &client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_WILDCARD, JOB_UPDATE_TOPIC, JOB_ACCEPTED_REPLY_TYPE, + iot_update_accepted_callback_handler, NULL, topicToSubscribeUpdateAccepted, sizeof(topicToSubscribeUpdateAccepted)); + + if(SUCCESS != rc) { + IOT_ERROR("Error subscribing JOB_UPDATE_TOPIC/accepted: %d ", rc); + return rc; + } + + rc = aws_iot_jobs_subscribe_to_job_messages( + &client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_WILDCARD, JOB_UPDATE_TOPIC, JOB_REJECTED_REPLY_TYPE, + iot_update_rejected_callback_handler, NULL, topicToSubscribeUpdateRejected, sizeof(topicToSubscribeUpdateRejected)); + + if(SUCCESS != rc) { + IOT_ERROR("Error subscribing JOB_UPDATE_TOPIC/rejected: %d ", rc); + return rc; + } + + paramsQOS0.qos = QOS0; + paramsQOS0.payload = (void *) cPayload; + paramsQOS0.isRetained = 0; + paramsQOS0.payloadLen = strlen(cPayload); + + rc = aws_iot_jobs_send_query(&client, QOS0, AWS_IOT_MY_THING_NAME, NULL, NULL, topicToPublishGetPending, sizeof(topicToPublishGetPending), NULL, 0, JOB_GET_PENDING_TOPIC); + + AwsIotDescribeJobExecutionRequest describeRequest; + describeRequest.executionNumber = 0; + describeRequest.includeJobDocument = true; + describeRequest.clientToken = NULL; + + rc = aws_iot_jobs_describe(&client, QOS0, AWS_IOT_MY_THING_NAME, JOB_ID_NEXT, &describeRequest, topicToPublishGetNext, sizeof(topicToPublishGetNext), NULL, 0); + + while(SUCCESS == rc) { + //Max time the yield function will wait for read messages + rc = aws_iot_mqtt_yield(&client, 50000); + } + + return rc; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/shadow_sample/Makefile Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,71 @@ +#This target is to ensure accidental execution of Makefile as a bash script will not execute commands like rm in unexpected directories and exit gracefully. +.prevent_execution: + exit 0 + +CC = gcc + +#remove @ for no make command prints +DEBUG = @ + +APP_DIR = . +APP_INCLUDE_DIRS += -I $(APP_DIR) +APP_NAME = shadow_sample +APP_SRC_FILES = $(APP_NAME).c + +#IoT client directory +IOT_CLIENT_DIR = ../../.. + +PLATFORM_DIR = $(IOT_CLIENT_DIR)/platform/linux/mbedtls +PLATFORM_COMMON_DIR = $(IOT_CLIENT_DIR)/platform/linux/common + +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/include +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/external_libs/jsmn +IOT_INCLUDE_DIRS += -I $(PLATFORM_COMMON_DIR) +IOT_INCLUDE_DIRS += -I $(PLATFORM_DIR) + +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/src/ -name '*.c') +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/external_libs/jsmn -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_DIR)/ -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_COMMON_DIR)/ -name '*.c') + +#TLS - mbedtls +MBEDTLS_DIR = $(IOT_CLIENT_DIR)/external_libs/mbedTLS +TLS_LIB_DIR = $(MBEDTLS_DIR)/library +TLS_INCLUDE_DIR = -I $(MBEDTLS_DIR)/include +EXTERNAL_LIBS += -L$(TLS_LIB_DIR) +LD_FLAG += -Wl,-rpath,$(TLS_LIB_DIR) +LD_FLAG += -ldl $(TLS_LIB_DIR)/libmbedtls.a $(TLS_LIB_DIR)/libmbedcrypto.a $(TLS_LIB_DIR)/libmbedx509.a + + +#Aggregate all include and src directories +INCLUDE_ALL_DIRS += $(IOT_INCLUDE_DIRS) +INCLUDE_ALL_DIRS += $(TLS_INCLUDE_DIR) +INCLUDE_ALL_DIRS += $(APP_INCLUDE_DIRS) + +SRC_FILES += $(APP_SRC_FILES) +SRC_FILES += $(IOT_SRC_FILES) + +# Logging level control +LOG_FLAGS += -DENABLE_IOT_DEBUG +LOG_FLAGS += -DENABLE_IOT_INFO +LOG_FLAGS += -DENABLE_IOT_WARN +LOG_FLAGS += -DENABLE_IOT_ERROR + +COMPILER_FLAGS += -g +COMPILER_FLAGS += $(LOG_FLAGS) +#If the processor is big endian uncomment the compiler flag +#COMPILER_FLAGS += -DREVERSED + +MBED_TLS_MAKE_CMD = $(MAKE) -C $(MBEDTLS_DIR) + +PRE_MAKE_CMD = $(MBED_TLS_MAKE_CMD) +MAKE_CMD = $(CC) $(SRC_FILES) $(COMPILER_FLAGS) -o $(APP_NAME) $(LD_FLAG) $(EXTERNAL_LIBS) $(INCLUDE_ALL_DIRS) + +all: + $(PRE_MAKE_CMD) + $(DEBUG)$(MAKE_CMD) + $(POST_MAKE_CMD) + +clean: + rm -f $(APP_DIR)/$(APP_NAME) + $(MBED_TLS_MAKE_CMD) clean
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/shadow_sample/aws_iot_config.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_config.h + * @brief AWS IoT specific configuration file + */ + +#ifndef SRC_SHADOW_IOT_SHADOW_CONFIG_H_ +#define SRC_SHADOW_IOT_SHADOW_CONFIG_H_ + +// Get from console +// ================================================= +#define AWS_IOT_MQTT_HOST "" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow +#define AWS_IOT_MQTT_PORT 443 ///< default port for MQTT/S +#define AWS_IOT_MQTT_CLIENT_ID "c-sdk-client-id" ///< MQTT client ID should be unique for every device +#define AWS_IOT_MY_THING_NAME "AWS-IoT-C-SDK" ///< Thing Name of the Shadow this device is associated with +#define AWS_IOT_ROOT_CA_FILENAME "rootCA.crt" ///< Root CA file name +#define AWS_IOT_CERTIFICATE_FILENAME "cert.pem" ///< device signed certificate file name +#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem" ///< Device private key filename +// ================================================= + +// MQTT PubSub +#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow +#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped. +#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow + +// Thing Shadow specific configs +#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN+1) ///< Maximum size of the SHADOW buffer to store the received Shadow message, including terminating NULL byte. +#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments" +#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 ///< This is size of the extra sequence number that will be appended to the Unique client Id +#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 ///< This is size of the the total clientToken key and value pair in the JSON +#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested +#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time +#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published +#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name +#define MAX_SIZE_OF_THING_NAME 20 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger +#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME ///< This size includes the length of topic with Thing Name + +// Auto Reconnect specific config +#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm +#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect. + +#define DISABLE_METRICS false ///< Disable the collection of metrics by setting this to true + +#endif /* SRC_SHADOW_IOT_SHADOW_CONFIG_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/shadow_sample/shadow_sample.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,275 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file shadow_sample.c + * @brief A simple connected window example demonstrating the use of Thing Shadow + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> +#include <string.h> +#include <limits.h> + +#include "aws_iot_config.h" +#include "aws_iot_log.h" +#include "aws_iot_version.h" +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_shadow_interface.h" + +/*! + * The goal of this sample application is to demonstrate the capabilities of shadow. + * This device(say Connected Window) will open the window of a room based on temperature + * It can report to the Shadow the following parameters: + * 1. temperature of the room (double) + * 2. status of the window (open or close) + * It can act on commands from the cloud. In this case it will open or close the window based on the json object "windowOpen" data[open/close] + * + * The two variables from a device's perspective are double temperature and bool windowOpen + * The device needs to act on only on windowOpen variable, so we will create a primitiveJson_t object with callback + The Json Document in the cloud will be + { + "reported": { + "temperature": 0, + "windowOpen": false + }, + "desired": { + "windowOpen": false + } + } + */ + +#define ROOMTEMPERATURE_UPPERLIMIT 32.0f +#define ROOMTEMPERATURE_LOWERLIMIT 25.0f +#define STARTING_ROOMTEMPERATURE ROOMTEMPERATURE_LOWERLIMIT + +#define MAX_LENGTH_OF_UPDATE_JSON_BUFFER 200 + +static char certDirectory[PATH_MAX + 1] = "../../../certs"; +#define HOST_ADDRESS_SIZE 255 +static char HostAddress[HOST_ADDRESS_SIZE] = AWS_IOT_MQTT_HOST; +static uint32_t port = AWS_IOT_MQTT_PORT; +static uint8_t numPubs = 5; + +static void simulateRoomTemperature(float *pRoomTemperature) { + static float deltaChange; + + if(*pRoomTemperature >= ROOMTEMPERATURE_UPPERLIMIT) { + deltaChange = -0.5f; + } else if(*pRoomTemperature <= ROOMTEMPERATURE_LOWERLIMIT) { + deltaChange = 0.5f; + } + + *pRoomTemperature += deltaChange; +} + +void ShadowUpdateStatusCallback(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status, + const char *pReceivedJsonDocument, void *pContextData) { + IOT_UNUSED(pThingName); + IOT_UNUSED(action); + IOT_UNUSED(pReceivedJsonDocument); + IOT_UNUSED(pContextData); + + if(SHADOW_ACK_TIMEOUT == status) { + IOT_INFO("Update Timeout--"); + } else if(SHADOW_ACK_REJECTED == status) { + IOT_INFO("Update RejectedXX"); + } else if(SHADOW_ACK_ACCEPTED == status) { + IOT_INFO("Update Accepted !!"); + } +} + +void windowActuate_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) { + IOT_UNUSED(pJsonString); + IOT_UNUSED(JsonStringDataLen); + + if(pContext != NULL) { + IOT_INFO("Delta - Window state changed to %d", *(bool *) (pContext->pData)); + } +} + +void parseInputArgsForConnectParams(int argc, char **argv) { + int opt; + + while(-1 != (opt = getopt(argc, argv, "h:p:c:n:"))) { + switch(opt) { + case 'h': + strncpy(HostAddress, optarg, HOST_ADDRESS_SIZE); + IOT_DEBUG("Host %s", optarg); + break; + case 'p': + port = atoi(optarg); + IOT_DEBUG("arg %s", optarg); + break; + case 'c': + strncpy(certDirectory, optarg, PATH_MAX + 1); + IOT_DEBUG("cert root directory %s", optarg); + break; + case 'n': + numPubs = atoi(optarg); + IOT_DEBUG("num pubs %s", optarg); + break; + case '?': + if(optopt == 'c') { + IOT_ERROR("Option -%c requires an argument.", optopt); + } else if(isprint(optopt)) { + IOT_WARN("Unknown option `-%c'.", optopt); + } else { + IOT_WARN("Unknown option character `\\x%x'.", optopt); + } + break; + default: + IOT_ERROR("ERROR in command line argument parsing"); + break; + } + } + +} + +int main(int argc, char **argv) { + IoT_Error_t rc = FAILURE; + int32_t i = 0; + + char JsonDocumentBuffer[MAX_LENGTH_OF_UPDATE_JSON_BUFFER]; + size_t sizeOfJsonDocumentBuffer = sizeof(JsonDocumentBuffer) / sizeof(JsonDocumentBuffer[0]); + char *pJsonStringToUpdate; + float temperature = 0.0; + + bool windowOpen = false; + jsonStruct_t windowActuator; + windowActuator.cb = windowActuate_Callback; + windowActuator.pData = &windowOpen; + windowActuator.dataLength = sizeof(bool); + windowActuator.pKey = "windowOpen"; + windowActuator.type = SHADOW_JSON_BOOL; + + jsonStruct_t temperatureHandler; + temperatureHandler.cb = NULL; + temperatureHandler.pKey = "temperature"; + temperatureHandler.pData = &temperature; + temperatureHandler.dataLength = sizeof(float); + temperatureHandler.type = SHADOW_JSON_FLOAT; + + char rootCA[PATH_MAX + 1]; + char clientCRT[PATH_MAX + 1]; + char clientKey[PATH_MAX + 1]; + char CurrentWD[PATH_MAX + 1]; + + IOT_INFO("\nAWS IoT SDK Version %d.%d.%d-%s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG); + + getcwd(CurrentWD, sizeof(CurrentWD)); + snprintf(rootCA, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_ROOT_CA_FILENAME); + snprintf(clientCRT, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_CERTIFICATE_FILENAME); + snprintf(clientKey, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_PRIVATE_KEY_FILENAME); + + IOT_DEBUG("rootCA %s", rootCA); + IOT_DEBUG("clientCRT %s", clientCRT); + IOT_DEBUG("clientKey %s", clientKey); + + parseInputArgsForConnectParams(argc, argv); + + // initialize the mqtt client + AWS_IoT_Client mqttClient; + + ShadowInitParameters_t sp = ShadowInitParametersDefault; + sp.pHost = AWS_IOT_MQTT_HOST; + sp.port = AWS_IOT_MQTT_PORT; + sp.pClientCRT = clientCRT; + sp.pClientKey = clientKey; + sp.pRootCA = rootCA; + sp.enableAutoReconnect = false; + sp.disconnectHandler = NULL; + + IOT_INFO("Shadow Init"); + rc = aws_iot_shadow_init(&mqttClient, &sp); + if(SUCCESS != rc) { + IOT_ERROR("Shadow Connection Error"); + return rc; + } + + ShadowConnectParameters_t scp = ShadowConnectParametersDefault; + scp.pMyThingName = AWS_IOT_MY_THING_NAME; + scp.pMqttClientId = AWS_IOT_MQTT_CLIENT_ID; + scp.mqttClientIdLen = (uint16_t) strlen(AWS_IOT_MQTT_CLIENT_ID); + + IOT_INFO("Shadow Connect"); + rc = aws_iot_shadow_connect(&mqttClient, &scp); + if(SUCCESS != rc) { + IOT_ERROR("Shadow Connection Error"); + return rc; + } + + /* + * Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h + * #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL + * #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL + */ + rc = aws_iot_shadow_set_autoreconnect_status(&mqttClient, true); + if(SUCCESS != rc) { + IOT_ERROR("Unable to set Auto Reconnect to true - %d", rc); + return rc; + } + + rc = aws_iot_shadow_register_delta(&mqttClient, &windowActuator); + + if(SUCCESS != rc) { + IOT_ERROR("Shadow Register Delta Error"); + } + temperature = STARTING_ROOMTEMPERATURE; + + // loop and publish a change in temperature + while(NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || SUCCESS == rc) { + rc = aws_iot_shadow_yield(&mqttClient, 200); + if(NETWORK_ATTEMPTING_RECONNECT == rc) { + sleep(1); + // If the client is attempting to reconnect we will skip the rest of the loop. + continue; + } + IOT_INFO("\n=======================================================================================\n"); + IOT_INFO("On Device: window state %s", windowOpen ? "true" : "false"); + simulateRoomTemperature(&temperature); + + rc = aws_iot_shadow_init_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer); + if(SUCCESS == rc) { + rc = aws_iot_shadow_add_reported(JsonDocumentBuffer, sizeOfJsonDocumentBuffer, 2, &temperatureHandler, + &windowActuator); + if(SUCCESS == rc) { + rc = aws_iot_finalize_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer); + if(SUCCESS == rc) { + IOT_INFO("Update Shadow: %s", JsonDocumentBuffer); + rc = aws_iot_shadow_update(&mqttClient, AWS_IOT_MY_THING_NAME, JsonDocumentBuffer, + ShadowUpdateStatusCallback, NULL, 4, true); + } + } + } + IOT_INFO("*****************************************************************************************\n"); + sleep(1); + } + + if(SUCCESS != rc) { + IOT_ERROR("An error occurred in the loop %d", rc); + } + + IOT_INFO("Disconnecting"); + rc = aws_iot_shadow_disconnect(&mqttClient); + + if(SUCCESS != rc) { + IOT_ERROR("Disconnect error %d", rc); + } + + return rc; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/shadow_sample_console_echo/Makefile Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,74 @@ +#This target is to ensure accidental execution of Makefile as a bash script will not execute commands like rm in unexpected directories and exit gracefully. +.prevent_execution: + exit 0 + +CC = gcc + +#remove @ for no make command prints +DEBUG = @ + +APP_DIR = . +APP_INCLUDE_DIRS += -I $(APP_DIR) +APP_NAME = shadow_console_echo +APP_SRC_FILES = $(APP_NAME).c + +#IoT client directory +IOT_CLIENT_DIR = ../../.. + +PLATFORM_DIR = $(IOT_CLIENT_DIR)/platform/linux/mbedtls +PLATFORM_COMMON_DIR = $(IOT_CLIENT_DIR)/platform/linux/common + +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/include +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/external_libs/jsmn +IOT_INCLUDE_DIRS += -I $(PLATFORM_COMMON_DIR) +IOT_INCLUDE_DIRS += -I $(PLATFORM_DIR) + +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/src/ -name '*.c') +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/external_libs/jsmn -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_DIR)/ -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_COMMON_DIR)/ -name '*.c') + +#TLS - mbedtls +MBEDTLS_DIR = $(IOT_CLIENT_DIR)/external_libs/mbedTLS +TLS_LIB_DIR = $(MBEDTLS_DIR)/library +TLS_INCLUDE_DIR = -I $(MBEDTLS_DIR)/include +EXTERNAL_LIBS += -L$(TLS_LIB_DIR) +LD_FLAG += -Wl,-rpath,$(TLS_LIB_DIR) +LD_FLAG += -ldl $(TLS_LIB_DIR)/libmbedtls.a $(TLS_LIB_DIR)/libmbedcrypto.a $(TLS_LIB_DIR)/libmbedx509.a + + +#Aggregate all include and src directories +INCLUDE_ALL_DIRS += $(IOT_INCLUDE_DIRS) +INCLUDE_ALL_DIRS += $(MQTT_INCLUDE_DIR) +INCLUDE_ALL_DIRS += $(TLS_INCLUDE_DIR) +INCLUDE_ALL_DIRS += $(APP_INCLUDE_DIRS) + +SRC_FILES += $(MQTT_SRC_FILES) +SRC_FILES += $(APP_SRC_FILES) +SRC_FILES += $(IOT_SRC_FILES) + +# Logging level control +LOG_FLAGS += -DENABLE_IOT_DEBUG +LOG_FLAGS += -DENABLE_IOT_INFO +LOG_FLAGS += -DENABLE_IOT_WARN +LOG_FLAGS += -DENABLE_IOT_ERROR + +COMPILER_FLAGS += -g +COMPILER_FLAGS += $(LOG_FLAGS) + +#If the processor is big endian uncomment the compiler flag +#COMPILER_FLAGS += -DREVERSED + +MBED_TLS_MAKE_CMD = $(MAKE) -C $(MBEDTLS_DIR) + +PRE_MAKE_CMD = $(MBED_TLS_MAKE_CMD) +MAKE_CMD = $(CC) $(SRC_FILES) $(COMPILER_FLAGS) -o $(APP_NAME) $(LD_FLAG) $(EXTERNAL_LIBS) $(INCLUDE_ALL_DIRS) + +all: + $(PRE_MAKE_CMD) + $(DEBUG)$(MAKE_CMD) + $(POST_MAKE_CMD) + +clean: + rm -f $(APP_DIR)/$(APP_NAME) + $(MBED_TLS_MAKE_CMD) clean
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/shadow_sample_console_echo/aws_iot_config.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_config.h + * @brief AWS IoT specific configuration file + */ + +#ifndef SRC_SHADOW_IOT_SHADOW_CONFIG_H_ +#define SRC_SHADOW_IOT_SHADOW_CONFIG_H_ + +// Get from console +// ================================================= +#define AWS_IOT_MQTT_HOST "" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow +#define AWS_IOT_MQTT_PORT 443 ///< default port for MQTT/S +#define AWS_IOT_MQTT_CLIENT_ID "c-sdk-client-id" ///< MQTT client ID should be unique for every device +#define AWS_IOT_MY_THING_NAME "AWS-IoT-C-SDK" ///< Thing Name of the Shadow this device is associated with +#define AWS_IOT_ROOT_CA_FILENAME "rootCA.crt" ///< Root CA file name +#define AWS_IOT_CERTIFICATE_FILENAME "cert.pem" ///< device signed certificate file name +#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem" ///< Device private key filename +// ================================================= + +// MQTT PubSub +#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow +#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped. +#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow + +// Thing Shadow specific configs +#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN+1) ///< Maximum size of the SHADOW buffer to store the received Shadow message, including terminating NULL byte. +#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments" +#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 ///< This is size of the extra sequence number that will be appended to the Unique client Id +#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 ///< This is size of the the total clientToken key and value pair in the JSON +#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested +#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time +#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published +#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name +#define MAX_SIZE_OF_THING_NAME 20 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger +#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME ///< This size includes the length of topic with Thing Name + +// Auto Reconnect specific config +#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm +#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect. + +#define DISABLE_METRICS false ///< Disable the collection of metrics by setting this to true + +#endif /* SRC_SHADOW_IOT_SHADOW_CONFIG_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/shadow_sample_console_echo/shadow_console_echo.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,278 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <limits.h> + +#include "aws_iot_config.h" +#include "aws_iot_log.h" +#include "aws_iot_version.h" +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_shadow_interface.h" + +/** + * @file shadow_console_echo.c + * @brief Echo received Delta message + * + * This application will echo the message received in delta, as reported. + * for example: + * Received Delta message + * { + * "state": { + * "switch": "on" + * } + * } + * This delta message means the desired switch position has changed to "on" + * + * This application will take this delta message and publish it back as the reported message from the device. + * { + * "state": { + * "reported": { + * "switch": "on" + * } + * } + * } + * + * This update message will remove the delta that was created. If this message was not removed then the AWS IoT Thing Shadow is going to always have a delta and keep sending delta any time an update is applied to the Shadow + * This example will not use any of the json builder/helper functions provided in the aws_iot_shadow_json_data.h. + * @note Ensure the buffer sizes in aws_iot_config.h are big enough to receive the delta message. The delta message will also contain the metadata with the timestamps + */ + +char certDirectory[PATH_MAX + 1] = "../../../certs"; +#define HOST_ADDRESS_SIZE 255 +char HostAddress[HOST_ADDRESS_SIZE] = AWS_IOT_MQTT_HOST; +uint32_t port = AWS_IOT_MQTT_PORT; +bool messageArrivedOnDelta = false; + +/* + * @note The delta message is always sent on the "state" key in the json + * @note Any time messages are bigger than AWS_IOT_MQTT_RX_BUF_LEN the underlying MQTT library will ignore it. The maximum size of the message that can be received is limited to the AWS_IOT_MQTT_RX_BUF_LEN + */ +char stringToEchoDelta[SHADOW_MAX_SIZE_OF_RX_BUFFER]; + +// Helper functions +void parseInputArgsForConnectParams(int argc, char** argv); + +// Shadow Callback for receiving the delta +void DeltaCallback(const char *pJsonValueBuffer, uint32_t valueLength, jsonStruct_t *pJsonStruct_t); + +void UpdateStatusCallback(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status, + const char *pReceivedJsonDocument, void *pContextData); + +int main(int argc, char** argv) { + IoT_Error_t rc = SUCCESS; + int32_t i = 0; + + char rootCA[PATH_MAX + 1]; + char clientCRT[PATH_MAX + 1]; + char clientKey[PATH_MAX + 1]; + char CurrentWD[PATH_MAX + 1]; + + IOT_INFO("\nAWS IoT SDK Version %d.%d.%d-%s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG); + + getcwd(CurrentWD, sizeof(CurrentWD)); + snprintf(rootCA, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_ROOT_CA_FILENAME); + snprintf(clientCRT, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_CERTIFICATE_FILENAME); + snprintf(clientKey, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_PRIVATE_KEY_FILENAME); + + IOT_DEBUG("rootCA %s", rootCA); + IOT_DEBUG("clientCRT %s", clientCRT); + IOT_DEBUG("clientKey %s", clientKey); + + parseInputArgsForConnectParams(argc, argv); + + // initialize the mqtt client + AWS_IoT_Client mqttClient; + + ShadowInitParameters_t sp = ShadowInitParametersDefault; + sp.pHost = AWS_IOT_MQTT_HOST; + sp.port = AWS_IOT_MQTT_PORT; + sp.pClientCRT = clientCRT; + sp.pClientKey = clientKey; + sp.pRootCA = rootCA; + sp.enableAutoReconnect = false; + sp.disconnectHandler = NULL; + + IOT_INFO("Shadow Init"); + rc = aws_iot_shadow_init(&mqttClient, &sp); + if (SUCCESS != rc) { + IOT_ERROR("Shadow Connection Error"); + return rc; + } + + ShadowConnectParameters_t scp = ShadowConnectParametersDefault; + scp.pMyThingName = AWS_IOT_MY_THING_NAME; + scp.pMqttClientId = AWS_IOT_MQTT_CLIENT_ID; + scp.mqttClientIdLen = (uint16_t) strlen(AWS_IOT_MQTT_CLIENT_ID); + + IOT_INFO("Shadow Connect"); + rc = aws_iot_shadow_connect(&mqttClient, &scp); + if (SUCCESS != rc) { + IOT_ERROR("Shadow Connection Error"); + return rc; + } + + /* + * Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h + * #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL + * #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL + */ + rc = aws_iot_shadow_set_autoreconnect_status(&mqttClient, true); + if(SUCCESS != rc){ + IOT_ERROR("Unable to set Auto Reconnect to true - %d", rc); + return rc; + } + + jsonStruct_t deltaObject; + deltaObject.pData = stringToEchoDelta; + deltaObject.dataLength = SHADOW_MAX_SIZE_OF_RX_BUFFER; + deltaObject.pKey = "state"; + deltaObject.type = SHADOW_JSON_OBJECT; + deltaObject.cb = DeltaCallback; + + /* + * Register the jsonStruct object + */ + rc = aws_iot_shadow_register_delta(&mqttClient, &deltaObject); + + // Now wait in the loop to receive any message sent from the console + while (NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || SUCCESS == rc) { + /* + * Lets check for the incoming messages for 200 ms. + */ + rc = aws_iot_shadow_yield(&mqttClient, 200); + + if (NETWORK_ATTEMPTING_RECONNECT == rc) { + sleep(1); + // If the client is attempting to reconnect we will skip the rest of the loop. + continue; + } + + if (messageArrivedOnDelta) { + IOT_INFO("\nSending delta message back %s\n", stringToEchoDelta); + rc = aws_iot_shadow_update(&mqttClient, AWS_IOT_MY_THING_NAME, stringToEchoDelta, UpdateStatusCallback, NULL, 2, true); + messageArrivedOnDelta = false; + } + + // sleep for some time in seconds + sleep(1); + } + + if (SUCCESS != rc) { + IOT_ERROR("An error occurred in the loop %d", rc); + } + + IOT_INFO("Disconnecting"); + rc = aws_iot_shadow_disconnect(&mqttClient); + + if (SUCCESS != rc) { + IOT_ERROR("Disconnect error %d", rc); + } + + return rc; +} +/** + * @brief This function builds a full Shadow expected JSON document by putting the data in the reported section + * + * @param pJsonDocument Buffer to be filled up with the JSON data + * @param maxSizeOfJsonDocument maximum size of the buffer that could be used to fill + * @param pReceivedDeltaData This is the data that will be embedded in the reported section of the JSON document + * @param lengthDelta Length of the data + */ +bool buildJSONForReported(char *pJsonDocument, size_t maxSizeOfJsonDocument, const char *pReceivedDeltaData, uint32_t lengthDelta) { + int32_t ret; + + if (NULL == pJsonDocument) { + return false; + } + + char tempClientTokenBuffer[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE]; + + if(aws_iot_fill_with_client_token(tempClientTokenBuffer, MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE) != SUCCESS){ + return false; + } + + ret = snprintf(pJsonDocument, maxSizeOfJsonDocument, "{\"state\":{\"reported\":%.*s}, \"clientToken\":\"%s\"}", lengthDelta, pReceivedDeltaData, tempClientTokenBuffer); + + if (ret >= maxSizeOfJsonDocument || ret < 0) { + return false; + } + + return true; +} + +void parseInputArgsForConnectParams(int argc, char** argv) { + int opt; + + while (-1 != (opt = getopt(argc, argv, "h:p:c:"))) { + switch (opt) { + case 'h': + strncpy(HostAddress, optarg, HOST_ADDRESS_SIZE); + IOT_DEBUG("Host %s", optarg); + break; + case 'p': + port = atoi(optarg); + IOT_DEBUG("arg %s", optarg); + break; + case 'c': + strncpy(certDirectory, optarg, PATH_MAX + 1); + IOT_DEBUG("cert root directory %s", optarg); + break; + case '?': + if (optopt == 'c') { + IOT_ERROR("Option -%c requires an argument.", optopt); + } else if (isprint(optopt)) { + IOT_WARN("Unknown option `-%c'.", optopt); + } else { + IOT_WARN("Unknown option character `\\x%x'.", optopt); + } + break; + default: + IOT_ERROR("ERROR in command line argument parsing"); + break; + } + } + +} + + +void DeltaCallback(const char *pJsonValueBuffer, uint32_t valueLength, jsonStruct_t *pJsonStruct_t) { + IOT_UNUSED(pJsonStruct_t); + + IOT_DEBUG("Received Delta message %.*s", valueLength, pJsonValueBuffer); + + if (buildJSONForReported(stringToEchoDelta, SHADOW_MAX_SIZE_OF_RX_BUFFER, pJsonValueBuffer, valueLength)) { + messageArrivedOnDelta = true; + } +} + +void UpdateStatusCallback(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status, + const char *pReceivedJsonDocument, void *pContextData) { + IOT_UNUSED(pThingName); + IOT_UNUSED(action); + IOT_UNUSED(pReceivedJsonDocument); + IOT_UNUSED(pContextData); + + if(SHADOW_ACK_TIMEOUT == status) { + IOT_INFO("Update Timeout--"); + } else if(SHADOW_ACK_REJECTED == status) { + IOT_INFO("Update RejectedXX"); + } else if(SHADOW_ACK_ACCEPTED == status) { + IOT_INFO("Update Accepted !!"); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/subscribe_publish_cpp_sample/aws_iot_config.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_config.h + * @brief AWS IoT specific configuration file + */ + +#ifndef SRC_SHADOW_IOT_SHADOW_CONFIG_H_ +#define SRC_SHADOW_IOT_SHADOW_CONFIG_H_ + +//#define _ENABLE_THREAD_SUPPORT_ + +// Get from console +// ================================================= +#define AWS_IOT_MQTT_HOST (char*)"a1075elwyht7bd.iot.us-west-2.amazonaws.com" +#define AWS_IOT_MQTT_PORT 443 +#define AWS_IOT_MQTT_CLIENT_ID (char*)"c-sdk-client-id" +#define AWS_IOT_MY_THING_NAME (char*)"AWS-IoT-C-SDK" +#define AWS_IOT_ROOT_CA_FILENAME (char*)"rootCA.crt" +#define AWS_IOT_CERTIFICATE_FILENAME (char*)"cert.pem" +#define AWS_IOT_PRIVATE_KEY_FILENAME (char*)"privkey.pem" +// ================================================= + +// MQTT PubSub +#define AWS_IOT_MQTT_TX_BUF_LEN 512 +#define AWS_IOT_MQTT_RX_BUF_LEN 512 +#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 + +// Thing Shadow specific configs +#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN+1) +#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 +#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 +#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 +#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 +#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 +#define MAX_JSON_TOKEN_EXPECTED 120 +#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 +#define MAX_SIZE_OF_THING_NAME 20 +#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME + +// Auto Reconnect specific config +#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 +#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 + +#define DISABLE_METRICS false + +#endif /* SRC_SHADOW_IOT_SHADOW_CONFIG_H_ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/subscribe_publish_cpp_sample/subscribe_publish_cpp_sample.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,192 @@ +/** + * @file subscribe_publish_cpp_sample.cpp + * @brief simple MQTT publish and subscribe on the same topic in C++ + * + * This example takes the parameters from the aws_iot_config.h file and establishes a connection to the AWS IoT MQTT Platform. + * It subscribes and publishes to the same topic - "sdkTest/sub" + * + * If all the certs are correct, you should see the messages received by the application in a loop. + * + * The application takes in the certificate path, host name , port and the number of times the publish should happen. + * + */ +#include "mbed.h" + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <limits.h> +#include <string.h> + +#include "aws_iot_config.h" +#include "aws_iot_log.h" +#include "aws_iot_version.h" +#include "aws_iot_mqtt_client_interface.h" + +#include "easy-connect.h" + +/** + * @brief Default cert location + */ +char cPayload[100]; + +Thread aws_subscribe_publish(osPriorityNormal, 8*1024, NULL); +void aws_subscribe_publish_task(void); + +/** + * @brief This parameter will avoid infinite loop of publish and exit the program after certain number of publishes + */ +uint32_t publishCount = 0; + +void iot_subscribe_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, IoT_Publish_Message_Params *params, void *pData) +{ + IOT_UNUSED(pData); + IOT_UNUSED(pClient); + IOT_INFO("Subscribe callback"); + IOT_INFO("%.*s\t%.*s", topicNameLen, topicName, (int) params->payloadLen, (char *) params->payload); +} + +void disconnectCallbackHandler(AWS_IoT_Client *pClient, void *data) +{ + IOT_WARN("MQTT Disconnect"); + IoT_Error_t rc = FAILURE; + + if(NULL == pClient) + return; + + IOT_UNUSED(data); + + if(aws_iot_is_autoreconnect_enabled(pClient)) { + IOT_INFO("Auto Reconnect is enabled, Reconnecting attempt will start now"); + } + else{ + IOT_WARN("Auto Reconnect not enabled. Starting manual reconnect..."); + rc = aws_iot_mqtt_attempt_reconnect(pClient); + if(NETWORK_RECONNECTED == rc) { + IOT_WARN("Manual Reconnect Successful"); + } + else{ + IOT_WARN("Manual Reconnect Failed - %d", rc); + } + } +} + +int main() +{ + printf("AWS %s Example.\n",__FILE__); + IOT_INFO("\nAWS IoT SDK Version %d.%d.%d-%s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG); + + aws_subscribe_publish.start(aws_subscribe_publish_task); + aws_subscribe_publish.join(); + printf(" - - - - - - - ALL DONE - - - - - - - \n"); +} + +void aws_subscribe_publish_task() +{ + bool infinitePublishFlag = true; + IoT_Error_t rc = FAILURE; + + + int32_t i = 0; + + AWS_IoT_Client client; + IoT_Client_Init_Params mqttInitParams = iotClientInitParamsDefault; + IoT_Client_Connect_Params connectParams = iotClientConnectParamsDefault; + + IoT_Publish_Message_Params paramsQOS0; + IoT_Publish_Message_Params paramsQOS1; + + memset(cPayload, 0x00, sizeof(cPayload)); + + mqttInitParams.enableAutoReconnect = false; // We enable this later below + mqttInitParams.pHostURL = AWS_IOT_MQTT_HOST; + mqttInitParams.port = AWS_IOT_MQTT_PORT; + mqttInitParams.pRootCALocation = AWS_IOT_ROOT_CA_FILENAME; + mqttInitParams.pDeviceCertLocation = AWS_IOT_CERTIFICATE_FILENAME; + mqttInitParams.pDevicePrivateKeyLocation = AWS_IOT_PRIVATE_KEY_FILENAME; + mqttInitParams.mqttCommandTimeout_ms = 20000; + mqttInitParams.tlsHandshakeTimeout_ms = 5000; + mqttInitParams.isSSLHostnameVerify = true; + mqttInitParams.disconnectHandler = disconnectCallbackHandler; + mqttInitParams.disconnectHandlerData = NULL; + rc = aws_iot_mqtt_init(&client, &mqttInitParams); + if(AWS_SUCCESS != rc) { + IOT_ERROR("aws_iot_mqtt_init returned error : %d ", rc); + return; + } + + connectParams.keepAliveIntervalInSec = 600; + connectParams.isCleanSession = true; + connectParams.MQTTVersion = MQTT_3_1_1; + connectParams.pClientID = (char *)AWS_IOT_MQTT_CLIENT_ID; + connectParams.clientIDLen = (uint16_t) strlen(AWS_IOT_MQTT_CLIENT_ID); + connectParams.isWillMsgPresent = false; + + IOT_INFO("Connecting..."); + rc = aws_iot_mqtt_connect(&client, &connectParams); + if(AWS_SUCCESS != rc) { + IOT_ERROR("Error(%d) connecting to %s:%d", rc, mqttInitParams.pHostURL, mqttInitParams.port); + return; + } + + /* + * Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h + * #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL + * #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL + */ + rc = aws_iot_mqtt_autoreconnect_set_status(&client, true); + if(AWS_SUCCESS != rc) { + IOT_ERROR("Unable to set Auto Reconnect to true - %d", rc); + return; + } + + IOT_INFO("Subscribing..."); + rc = aws_iot_mqtt_subscribe(&client, "sdkTest/sub", 11, QOS0, iot_subscribe_callback_handler, NULL); + if(AWS_SUCCESS != rc) { + IOT_ERROR("Error subscribing : %d ", rc); + return; + } + + sprintf(cPayload, "%s : %ld ", "hello from SDK", i); + + paramsQOS0.qos = QOS0; + paramsQOS0.payload = (void *) cPayload; + paramsQOS0.isRetained = 0; + + paramsQOS1.qos = QOS1; + paramsQOS1.payload = (void *) cPayload; + paramsQOS1.isRetained = 0; + + if(publishCount != 0) { + infinitePublishFlag = false; + } + + while( (NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || AWS_SUCCESS == rc) && (publishCount > 0 || infinitePublishFlag)) { + sprintf(cPayload, "%s : %ld ", "hello from SDK QOS0", i++); + paramsQOS0.payloadLen = strlen(cPayload); + rc = aws_iot_mqtt_publish(&client, "sdkTest/sub", 11, ¶msQOS0); + if(publishCount > 0) + publishCount--; + + sprintf(cPayload, "%s : %ld ", "hello from SDK QOS1", i++); + paramsQOS1.payloadLen = strlen(cPayload); + rc = aws_iot_mqtt_publish(&client, "sdkTest/sub", 11, ¶msQOS1); + if (rc == MQTT_REQUEST_TIMEOUT_ERROR) { + IOT_WARN("QOS1 publish ack not received.\n"); + rc = AWS_SUCCESS; + } + if(publishCount > 0) + publishCount--; + + IOT_INFO("-->sleep"); + wait(5); + } + + if(AWS_SUCCESS != rc) { + IOT_ERROR("An error occurred in the loop.\n"); + } + else{ + IOT_INFO("Publish done\n"); + } + printf("... AWS_SUBSCRIBE_PUBLISH EXAMPLE DONE!\n"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/subscribe_publish_library_sample/Makefile Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,75 @@ +#This target is to ensure accidental execution of Makefile as a bash script will not execute commands like rm in unexpected directories and exit gracefully. +.prevent_execution: + exit 0 + +CC = gcc + +#remove @ for no make command prints +DEBUG = @ + +APP_DIR = . +APP_INCLUDE_DIRS += -I $(APP_DIR) +APP_NAME = subscribe_publish_library_sample +APP_SRC_FILES = $(APP_NAME).c + +#IoT client directory +IOT_CLIENT_DIR = ../../.. + +PLATFORM_DIR = $(IOT_CLIENT_DIR)/platform/linux/mbedtls +PLATFORM_COMMON_DIR = $(IOT_CLIENT_DIR)/platform/linux/common + +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/include +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/external_libs/jsmn +IOT_INCLUDE_DIRS += -I $(PLATFORM_COMMON_DIR) +IOT_INCLUDE_DIRS += -I $(PLATFORM_DIR) + +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/src/ -name '*.c') +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/external_libs/jsmn -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_DIR)/ -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_COMMON_DIR)/ -name '*.c') + +#TLS - mbedtls +MBEDTLS_DIR = $(IOT_CLIENT_DIR)/external_libs/mbedTLS +TLS_LIB_DIR = $(MBEDTLS_DIR)/library +TLS_INCLUDE_DIR = -I $(MBEDTLS_DIR)/include +EXTERNAL_LIBS += -L$(TLS_LIB_DIR) +LD_FLAG += -Wl,-rpath,$(TLS_LIB_DIR) +LD_FLAG += -ldl $(TLS_LIB_DIR)/libmbedtls.a $(TLS_LIB_DIR)/libmbedcrypto.a $(TLS_LIB_DIR)/libmbedx509.a -lpthread + +#Aggregate all include and src directories +INCLUDE_ALL_DIRS += $(IOT_INCLUDE_DIRS) +INCLUDE_ALL_DIRS += $(TLS_INCLUDE_DIR) +INCLUDE_ALL_DIRS += $(APP_INCLUDE_DIRS) + +SRC_FILES += $(IOT_SRC_FILES) + +# Logging level control +LOG_FLAGS += -DENABLE_IOT_DEBUG +LOG_FLAGS += -DENABLE_IOT_INFO +LOG_FLAGS += -DENABLE_IOT_WARN +LOG_FLAGS += -DENABLE_IOT_ERROR + +COMPILER_FLAGS += $(LOG_FLAGS) +#If the processor is big endian uncomment the compiler flag +#COMPILER_FLAGS += -DREVERSED + +MBED_TLS_MAKE_CMD = $(MAKE) -C $(MBEDTLS_DIR) + +PRE_MAKE_CMD = $(MBED_TLS_MAKE_CMD) +MAKE_CMD = $(CC) $(APP_NAME).c $(COMPILER_FLAGS) -o $(APP_NAME) -L. -lAwsIotSdk $(LD_FLAG) $(INCLUDE_ALL_DIRS) + +all: libAwsIotSdk.a + $(PRE_MAKE_CMD) + $(DEBUG)$(MAKE_CMD) + $(POST_MAKE_CMD) + +libAwsIotSdk.a: $(SRC_FILES:.c=.o) + ar rcs $@ $^ + +%.o : %.c + $(CC) -c $< -o $@ $(COMPILER_FLAGS) $(EXTERNAL_LIBS) $(INCLUDE_ALL_DIRS) + +clean: + rm -f $(APP_DIR)/$(APP_NAME) + rm -f $(APP_DIR)/libAwsIotSdk.a + $(MBED_TLS_MAKE_CMD) clean
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/subscribe_publish_library_sample/aws_iot_config.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_config.h + * @brief AWS IoT specific configuration file + */ + +#ifndef SRC_SHADOW_IOT_SHADOW_CONFIG_H_ +#define SRC_SHADOW_IOT_SHADOW_CONFIG_H_ + +// Get from console +// ================================================= +#define AWS_IOT_MQTT_HOST "" ///< Customer specific MQTT HOST. The same will be used for Thing Shadow +#define AWS_IOT_MQTT_PORT 443 ///< default port for MQTT/S +#define AWS_IOT_MQTT_CLIENT_ID "c-sdk-client-id" ///< MQTT client ID should be unique for every device +#define AWS_IOT_MY_THING_NAME "AWS-IoT-C-SDK" ///< Thing Name of the Shadow this device is associated with +#define AWS_IOT_ROOT_CA_FILENAME "rootCA.crt" ///< Root CA file name +#define AWS_IOT_CERTIFICATE_FILENAME "cert.pem" ///< device signed certificate file name +#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem" ///< Device private key filename +// ================================================= + +// MQTT PubSub +#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow +#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped. +#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow + +// Thing Shadow specific configs +#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN+1) ///< Maximum size of the SHADOW buffer to store the received Shadow message, including terminating NULL byte. +#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments" +#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 ///< This is size of the extra sequence number that will be appended to the Unique client Id +#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 ///< This is size of the the total clientToken key and value pair in the JSON +#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested +#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time +#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published +#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the format $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name +#define MAX_SIZE_OF_THING_NAME 20 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger +#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME ///< This size includes the length of topic with Thing Name + +// Auto Reconnect specific config +#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm +#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect. + +#define DISABLE_METRICS false ///< Disable the collection of metrics by setting this to true + +#endif /* SRC_SHADOW_IOT_SHADOW_CONFIG_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/subscribe_publish_library_sample/subscribe_publish_library_sample.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,264 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file subscribe_publish_library_sample.c + * @brief simple MQTT publish and subscribe on the same topic using the SDK as a library + * + * This example takes the parameters from the aws_iot_config.h file and establishes a connection to the AWS IoT MQTT Platform. + * It subscribes and publishes to the same topic - "sdkTest/sub" + * + * If all the certs are correct, you should see the messages received by the application in a loop. + * + * The application takes in the certificate path, host name , port and the number of times the publish should happen. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <unistd.h> +#include <limits.h> +#include <string.h> + +#include "aws_iot_config.h" +#include "aws_iot_log.h" +#include "aws_iot_version.h" +#include "aws_iot_mqtt_client_interface.h" + +#define HOST_ADDRESS_SIZE 255 + +/** + * @brief Default cert location + */ +char certDirectory[PATH_MAX + 1] = "../../../certs"; + +/** + * @brief Default MQTT HOST URL is pulled from the aws_iot_config.h + */ +char HostAddress[HOST_ADDRESS_SIZE] = AWS_IOT_MQTT_HOST; + +/** + * @brief Default MQTT port is pulled from the aws_iot_config.h + */ +uint32_t port = AWS_IOT_MQTT_PORT; + +/** + * @brief This parameter will avoid infinite loop of publish and exit the program after certain number of publishes + */ +uint32_t publishCount = 0; + +void iot_subscribe_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) { + IOT_UNUSED(pData); + IOT_UNUSED(pClient); + IOT_INFO("Subscribe callback"); + IOT_INFO("%.*s\t%.*s", topicNameLen, topicName, (int) params->payloadLen, (char *) params->payload); +} + +void disconnectCallbackHandler(AWS_IoT_Client *pClient, void *data) { + IOT_WARN("MQTT Disconnect"); + IoT_Error_t rc = FAILURE; + + if(NULL == pClient) { + return; + } + + IOT_UNUSED(data); + + if(aws_iot_is_autoreconnect_enabled(pClient)) { + IOT_INFO("Auto Reconnect is enabled, Reconnecting attempt will start now"); + } else { + IOT_WARN("Auto Reconnect not enabled. Starting manual reconnect..."); + rc = aws_iot_mqtt_attempt_reconnect(pClient); + if(NETWORK_RECONNECTED == rc) { + IOT_WARN("Manual Reconnect Successful"); + } else { + IOT_WARN("Manual Reconnect Failed - %d", rc); + } + } +} + +void parseInputArgsForConnectParams(int argc, char **argv) { + int opt; + + while(-1 != (opt = getopt(argc, argv, "h:p:c:x:"))) { + switch(opt) { + case 'h': + strncpy(HostAddress, optarg, HOST_ADDRESS_SIZE); + IOT_DEBUG("Host %s", optarg); + break; + case 'p': + port = atoi(optarg); + IOT_DEBUG("arg %s", optarg); + break; + case 'c': + strncpy(certDirectory, optarg, PATH_MAX + 1); + IOT_DEBUG("cert root directory %s", optarg); + break; + case 'x': + publishCount = atoi(optarg); + IOT_DEBUG("publish %s times\n", optarg); + break; + case '?': + if(optopt == 'c') { + IOT_ERROR("Option -%c requires an argument.", optopt); + } else if(isprint(optopt)) { + IOT_WARN("Unknown option `-%c'.", optopt); + } else { + IOT_WARN("Unknown option character `\\x%x'.", optopt); + } + break; + default: + IOT_ERROR("Error in command line argument parsing"); + break; + } + } + +} + +int main(int argc, char **argv) { + bool infinitePublishFlag = true; + + char rootCA[PATH_MAX + 1]; + char clientCRT[PATH_MAX + 1]; + char clientKey[PATH_MAX + 1]; + char CurrentWD[PATH_MAX + 1]; + char cPayload[100]; + + int32_t i = 0; + + IoT_Error_t rc = FAILURE; + + AWS_IoT_Client client; + IoT_Client_Init_Params mqttInitParams = iotClientInitParamsDefault; + IoT_Client_Connect_Params connectParams = iotClientConnectParamsDefault; + + IoT_Publish_Message_Params paramsQOS0; + IoT_Publish_Message_Params paramsQOS1; + + parseInputArgsForConnectParams(argc, argv); + + IOT_INFO("\nAWS IoT SDK Version %d.%d.%d-%s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG); + + getcwd(CurrentWD, sizeof(CurrentWD)); + snprintf(rootCA, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_ROOT_CA_FILENAME); + snprintf(clientCRT, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_CERTIFICATE_FILENAME); + snprintf(clientKey, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_PRIVATE_KEY_FILENAME); + + IOT_DEBUG("rootCA %s", rootCA); + IOT_DEBUG("clientCRT %s", clientCRT); + IOT_DEBUG("clientKey %s", clientKey); + mqttInitParams.enableAutoReconnect = false; // We enable this later below + mqttInitParams.pHostURL = HostAddress; + mqttInitParams.port = port; + mqttInitParams.pRootCALocation = rootCA; + mqttInitParams.pDeviceCertLocation = clientCRT; + mqttInitParams.pDevicePrivateKeyLocation = clientKey; + mqttInitParams.mqttCommandTimeout_ms = 20000; + mqttInitParams.tlsHandshakeTimeout_ms = 5000; + mqttInitParams.isSSLHostnameVerify = true; + mqttInitParams.disconnectHandler = disconnectCallbackHandler; + mqttInitParams.disconnectHandlerData = NULL; + + rc = aws_iot_mqtt_init(&client, &mqttInitParams); + if(SUCCESS != rc) { + IOT_ERROR("aws_iot_mqtt_init returned error : %d ", rc); + return rc; + } + + connectParams.keepAliveIntervalInSec = 600; + connectParams.isCleanSession = true; + connectParams.MQTTVersion = MQTT_3_1_1; + connectParams.pClientID = AWS_IOT_MQTT_CLIENT_ID; + connectParams.clientIDLen = (uint16_t) strlen(AWS_IOT_MQTT_CLIENT_ID); + connectParams.isWillMsgPresent = false; + + IOT_INFO("Connecting..."); + rc = aws_iot_mqtt_connect(&client, &connectParams); + if(SUCCESS != rc) { + IOT_ERROR("Error(%d) connecting to %s:%d", rc, mqttInitParams.pHostURL, mqttInitParams.port); + return rc; + } + /* + * Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h + * #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL + * #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL + */ + rc = aws_iot_mqtt_autoreconnect_set_status(&client, true); + if(SUCCESS != rc) { + IOT_ERROR("Unable to set Auto Reconnect to true - %d", rc); + return rc; + } + + IOT_INFO("Subscribing..."); + rc = aws_iot_mqtt_subscribe(&client, "sdkTest/sub", 11, QOS0, iot_subscribe_callback_handler, NULL); + if(SUCCESS != rc) { + IOT_ERROR("Error subscribing : %d ", rc); + return rc; + } + + sprintf(cPayload, "%s : %d ", "hello from SDK", i); + + paramsQOS0.qos = QOS0; + paramsQOS0.payload = (void *) cPayload; + paramsQOS0.isRetained = 0; + + paramsQOS1.qos = QOS1; + paramsQOS1.payload = (void *) cPayload; + paramsQOS1.isRetained = 0; + + if(publishCount != 0) { + infinitePublishFlag = false; + } + + while((NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || SUCCESS == rc) + && (publishCount > 0 || infinitePublishFlag)) { + + //Max time the yield function will wait for read messages + rc = aws_iot_mqtt_yield(&client, 100); + if(NETWORK_ATTEMPTING_RECONNECT == rc) { + // If the client is attempting to reconnect we will skip the rest of the loop. + continue; + } + + IOT_INFO("-->sleep"); + sleep(1); + sprintf(cPayload, "%s : %d ", "hello from SDK QOS0", i++); + paramsQOS0.payloadLen = strlen(cPayload); + rc = aws_iot_mqtt_publish(&client, "sdkTest/sub", 11, ¶msQOS0); + if(publishCount > 0) { + publishCount--; + } + + sprintf(cPayload, "%s : %d ", "hello from SDK QOS1", i++); + paramsQOS1.payloadLen = strlen(cPayload); + rc = aws_iot_mqtt_publish(&client, "sdkTest/sub", 11, ¶msQOS1); + if (rc == MQTT_REQUEST_TIMEOUT_ERROR) { + IOT_WARN("QOS1 publish ack not received.\n"); + rc = SUCCESS; + } + if(publishCount > 0) { + publishCount--; + } + } + + if(SUCCESS != rc) { + IOT_ERROR("An error occurred in the loop.\n"); + } else { + IOT_INFO("Publish done\n"); + } + + return rc; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/subscribe_publish_sample/aws_iot_config.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,61 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_config.h + * @brief AWS IoT specific configuration file + */ + +#ifndef SRC_SHADOW_IOT_SHADOW_CONFIG_H_ +#define SRC_SHADOW_IOT_SHADOW_CONFIG_H_ + +//#define _ENABLE_THREAD_SUPPORT_ + +// Get from console +// ================================================= +#define AWS_IOT_MQTT_HOST "a1075elwyht7bd.iot.us-west-2.amazonaws.com" +#define AWS_IOT_MQTT_PORT 443 +#define AWS_IOT_MQTT_CLIENT_ID (char*)"c-sdk-client-id" +#define AWS_IOT_MY_THING_NAME (char*)"AWS-IoT-C-SDK" +#define AWS_IOT_ROOT_CA_FILENAME (char*)"rootCA.crt" +#define AWS_IOT_CERTIFICATE_FILENAME (char*)"cert.pem" +#define AWS_IOT_PRIVATE_KEY_FILENAME (char*)"privkey.pem" +// ================================================= + +// MQTT PubSub +#define AWS_IOT_MQTT_TX_BUF_LEN 512 +#define AWS_IOT_MQTT_RX_BUF_LEN 512 +#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 + +// Thing Shadow specific configs +#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN+1) +#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 +#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10 +#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20 +#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 +#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 +#define MAX_JSON_TOKEN_EXPECTED 120 +#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 +#define MAX_SIZE_OF_THING_NAME 20 +#define MAX_SHADOW_TOPIC_LENGTH_BYTES MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME + +// Auto Reconnect specific config +#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 +#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 + +#define DISABLE_METRICS false + +#endif /* SRC_SHADOW_IOT_SHADOW_CONFIG_H_ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/subscribe_publish_sample/saved/Makefile Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,69 @@ +#This target is to ensure accidental execution of Makefile as a bash script will not execute commands like rm in unexpected directories and exit gracefully. +.prevent_execution: + exit 0 + +CC = gcc + +#remove @ for no make command prints +DEBUG = @ + +APP_DIR = . +APP_INCLUDE_DIRS += -I $(APP_DIR) +APP_NAME = subscribe_publish_sample +APP_SRC_FILES = $(APP_NAME).c + +#IoT client directory +IOT_CLIENT_DIR = ../../.. + +PLATFORM_DIR = $(IOT_CLIENT_DIR)/platform/linux/mbedtls +PLATFORM_COMMON_DIR = $(IOT_CLIENT_DIR)/platform/linux/common + +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/include +IOT_INCLUDE_DIRS += -I $(IOT_CLIENT_DIR)/external_libs/jsmn +IOT_INCLUDE_DIRS += -I $(PLATFORM_COMMON_DIR) +IOT_INCLUDE_DIRS += -I $(PLATFORM_DIR) + +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/src/ -name '*.c') +IOT_SRC_FILES += $(shell find $(IOT_CLIENT_DIR)/external_libs/jsmn -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_DIR)/ -name '*.c') +IOT_SRC_FILES += $(shell find $(PLATFORM_COMMON_DIR)/ -name '*.c') + +#TLS - mbedtls +MBEDTLS_DIR = $(IOT_CLIENT_DIR)/external_libs/mbedTLS +TLS_LIB_DIR = $(MBEDTLS_DIR)/library +TLS_INCLUDE_DIR = -I $(MBEDTLS_DIR)/include +EXTERNAL_LIBS += -L$(TLS_LIB_DIR) +LD_FLAG += -Wl,-rpath,$(TLS_LIB_DIR) +LD_FLAG += -ldl $(TLS_LIB_DIR)/libmbedtls.a $(TLS_LIB_DIR)/libmbedcrypto.a $(TLS_LIB_DIR)/libmbedx509.a -lpthread + +#Aggregate all include and src directories +INCLUDE_ALL_DIRS += $(IOT_INCLUDE_DIRS) +INCLUDE_ALL_DIRS += $(TLS_INCLUDE_DIR) +INCLUDE_ALL_DIRS += $(APP_INCLUDE_DIRS) + +SRC_FILES += $(APP_SRC_FILES) +SRC_FILES += $(IOT_SRC_FILES) + +# Logging level control +LOG_FLAGS += -DENABLE_IOT_DEBUG +LOG_FLAGS += -DENABLE_IOT_INFO +LOG_FLAGS += -DENABLE_IOT_WARN +LOG_FLAGS += -DENABLE_IOT_ERROR + +COMPILER_FLAGS += $(LOG_FLAGS) +#If the processor is big endian uncomment the compiler flag +#COMPILER_FLAGS += -DREVERSED + +MBED_TLS_MAKE_CMD = $(MAKE) -C $(MBEDTLS_DIR) + +PRE_MAKE_CMD = $(MBED_TLS_MAKE_CMD) +MAKE_CMD = $(CC) $(SRC_FILES) $(COMPILER_FLAGS) -o $(APP_NAME) $(LD_FLAG) $(EXTERNAL_LIBS) $(INCLUDE_ALL_DIRS) + +all: + $(PRE_MAKE_CMD) + $(DEBUG)$(MAKE_CMD) + $(POST_MAKE_CMD) + +clean: + rm -f $(APP_DIR)/$(APP_NAME) + $(MBED_TLS_MAKE_CMD) clean
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/subscribe_publish_sample/subscribe_publish_sample.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,249 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file subscribe_publish_sample.c + * @brief simple MQTT publish and subscribe on the same topic + * + * This example takes the parameters from the aws_iot_config.h file and establishes a connection to the AWS IoT MQTT Platform. + * It subscribes and publishes to the same topic - "sdkTest/sub" + * + * If all the certs are correct, you should see the messages received by the application in a loop. + * + * The application takes in the certificate path, host name , port and the number of times the publish should happen. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <limits.h> +#include <string.h> + +#include "aws_iot_config.h" +#include "aws_iot_log.h" +#include "aws_iot_version.h" +#include "aws_iot_mqtt_client_interface.h" + +#include "easy-connect.h" +#include "WNC14A2AInterface.h" + +#define HOST_ADDRESS_SIZE 255 + +Thread aws_subscribe_publish(osPriorityNormal, 16*1024, NULL); +void aws_subscribe_publish_task(void); + +/** + * @brief Default cert location + */ +char certDirectory[PATH_MAX + 1] = "../../../certs"; + +/** + * @brief Default MQTT HOST URL is pulled from the aws_iot_config.h + */ +char HostAddress[HOST_ADDRESS_SIZE] = AWS_IOT_MQTT_HOST; + +/** + * @brief Default MQTT port is pulled from the aws_iot_config.h + */ +uint32_t port = AWS_IOT_MQTT_PORT; + +/** + * @brief This parameter will avoid infinite loop of publish and exit the program after certain number of publishes + */ +uint32_t publishCount = 0; + +void iot_subscribe_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) { + IOT_UNUSED(pData); + IOT_UNUSED(pClient); + IOT_INFO("Subscribe callback"); + IOT_INFO("%.*s\t%.*s", topicNameLen, topicName, (int) params->payloadLen, (char *) params->payload); +} + +void disconnectCallbackHandler(AWS_IoT_Client *pClient, void *data) { + IOT_WARN("MQTT Disconnect"); + IoT_Error_t rc = FAILURE; + + if(NULL == pClient) { + return; + } + + IOT_UNUSED(data); + + if(aws_iot_is_autoreconnect_enabled(pClient)) { + IOT_INFO("Auto Reconnect is enabled, Reconnecting attempt will start now"); + } else { + IOT_WARN("Auto Reconnect not enabled. Starting manual reconnect..."); + rc = aws_iot_mqtt_attempt_reconnect(pClient); + if(NETWORK_RECONNECTED == rc) { + IOT_WARN("Manual Reconnect Successful"); + } else { + IOT_WARN("Manual Reconnect Failed - %d", rc); + } + } +} + + +int main() +{ + printf("AWS %s Example.\n",__FILE__); + IOT_INFO("\nAWS IoT SDK Version %d.%d.%d-%s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG); + + aws_subscribe_publish.start(aws_subscribe_publish_task); + aws_subscribe_publish.join(); + printf(" - - - - - - - ALL DONE - - - - - - - \n"); +} + +void aws_subscribe_publish_task() +{ + bool infinitePublishFlag = true; + + char rootCA[PATH_MAX + 1]; + char clientCRT[PATH_MAX + 1]; + char clientKey[PATH_MAX + 1]; + char CurrentWD[PATH_MAX + 1]; + char cPayload[100]; + + int32_t i = 0; + + IoT_Error_t rc = FAILURE; + + AWS_IoT_Client client; + IoT_Client_Init_Params mqttInitParams = iotClientInitParamsDefault; + IoT_Client_Connect_Params connectParams = iotClientConnectParamsDefault; + + IoT_Publish_Message_Params paramsQOS0; + IoT_Publish_Message_Params paramsQOS1; + + IOT_INFO("\nAWS IoT SDK Version %d.%d.%d-%s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG); + + snprintf(rootCA, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_ROOT_CA_FILENAME); + snprintf(clientCRT, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_CERTIFICATE_FILENAME); + snprintf(clientKey, PATH_MAX + 1, "%s/%s/%s", CurrentWD, certDirectory, AWS_IOT_PRIVATE_KEY_FILENAME); + + IOT_DEBUG("rootCA %s", rootCA); + IOT_DEBUG("clientCRT %s", clientCRT); + IOT_DEBUG("clientKey %s", clientKey); + mqttInitParams.enableAutoReconnect = false; // We enable this later below + mqttInitParams.pHostURL = HostAddress; + mqttInitParams.port = port; + mqttInitParams.pRootCALocation = rootCA; + mqttInitParams.pDeviceCertLocation = clientCRT; + mqttInitParams.pDevicePrivateKeyLocation = clientKey; + mqttInitParams.mqttCommandTimeout_ms = 20000; + mqttInitParams.tlsHandshakeTimeout_ms = 5000; + mqttInitParams.isSSLHostnameVerify = true; + mqttInitParams.disconnectHandler = disconnectCallbackHandler; + mqttInitParams.disconnectHandlerData = NULL; + + rc = aws_iot_mqtt_init(&client, &mqttInitParams); + if(SUCCESS != rc) { + IOT_ERROR("aws_iot_mqtt_init returned error : %d ", rc); + return; + } + + connectParams.keepAliveIntervalInSec = 600; + connectParams.isCleanSession = true; + connectParams.MQTTVersion = MQTT_3_1_1; + connectParams.pClientID = AWS_IOT_MQTT_CLIENT_ID; + connectParams.clientIDLen = (uint16_t) strlen(AWS_IOT_MQTT_CLIENT_ID); + connectParams.isWillMsgPresent = false; + + IOT_INFO("Connecting..."); + rc = aws_iot_mqtt_connect(&client, &connectParams); + if(SUCCESS != rc) { + IOT_ERROR("Error(%d) connecting to %s:%d", rc, mqttInitParams.pHostURL, mqttInitParams.port); + return; + } + /* + * Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h + * #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL + * #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL + */ + rc = aws_iot_mqtt_autoreconnect_set_status(&client, true); + if(SUCCESS != rc) { + IOT_ERROR("Unable to set Auto Reconnect to true - %d", rc); + return; + } + + IOT_INFO("Subscribing..."); + rc = aws_iot_mqtt_subscribe(&client, "sdkTest/sub", 11, QOS0, iot_subscribe_callback_handler, NULL); + if(SUCCESS != rc) { + IOT_ERROR("Error subscribing : %d ", rc); + return; + } + + sprintf(cPayload, "%s : %ld ", "hello from SDK", i); + + paramsQOS0.qos = QOS0; + paramsQOS0.payload = (void *) cPayload; + paramsQOS0.isRetained = 0; + + paramsQOS1.qos = QOS1; + paramsQOS1.payload = (void *) cPayload; + paramsQOS1.isRetained = 0; + + if(publishCount != 0) { + infinitePublishFlag = false; + } + + while((NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || SUCCESS == rc) + && (publishCount > 0 || infinitePublishFlag)) { + + //Max time the yield function will wait for read messages + rc = aws_iot_mqtt_yield(&client, 100); + if(NETWORK_ATTEMPTING_RECONNECT == rc) { + // If the client is attempting to reconnect we will skip the rest of the loop. + continue; + } + + IOT_INFO("-->sleep"); + wait(1); + sprintf(cPayload, "%s : %ld ", "hello from SDK QOS0", i++); + paramsQOS0.payloadLen = strlen(cPayload); + rc = aws_iot_mqtt_publish(&client, "sdkTest/sub", 11, ¶msQOS0); + if(publishCount > 0) { + publishCount--; + } + + if(publishCount == 0 && !infinitePublishFlag) { + break; + } + + sprintf(cPayload, "%s : %ld ", "hello from SDK QOS1", i++); + paramsQOS1.payloadLen = strlen(cPayload); + rc = aws_iot_mqtt_publish(&client, "sdkTest/sub", 11, ¶msQOS1); + if (rc == MQTT_REQUEST_TIMEOUT_ERROR) { + IOT_WARN("QOS1 publish ack not received.\n"); + rc = SUCCESS; + } + if(publishCount > 0) { + publishCount--; + } + } + + // Wait for all the messages to be received + aws_iot_mqtt_yield(&client, 100); + + if(SUCCESS != rc) { + IOT_ERROR("An error occurred in the loop.\n"); + } else { + IOT_INFO("Publish done\n"); + } + + return; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/external_libs/jsmn.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2010 Serge A. Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @file jsmn.c + * @brief Implementation of the JSMN (Jasmine) JSON parser. + * + * For more information on JSMN: + * @see http://zserge.com/jsmn.html + */ + +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/external_libs/jsmn.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2010 Serge A. Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @file jsmn.h + * @brief Definition of the JSMN (Jasmine) JSON parser. + * + * For more information on JSMN: + * @see http://zserge.com/jsmn.html + */ + +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_error.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,166 @@ +/* + * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_error.h + * @brief Definition of error types for the SDK. + */ + +#ifndef AWS_IOT_SDK_SRC_IOT_ERROR_H_ +#define AWS_IOT_SDK_SRC_IOT_ERROR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Used to avoid warnings in case of unused parameters in function pointers */ +#define IOT_UNUSED(x) (void)(x) + +/*! \public + * @brief IoT Error enum + * + * Enumeration of return values from the IoT_* functions within the SDK. + * Values less than -1 are specific error codes + * Value of -1 is a generic failure response + * Value of 0 is a generic success response + * Values greater than 0 are specific non-error return codes + */ +typedef enum { + /** Returned when the Network physical layer is connected */ + NETWORK_PHYSICAL_LAYER_CONNECTED = 6, + /** Returned when the Network is manually disconnected */ + NETWORK_MANUALLY_DISCONNECTED = 5, + /** Returned when the Network is disconnected and the reconnect attempt is in progress */ + NETWORK_ATTEMPTING_RECONNECT = 4, + /** Return value of yield function to indicate auto-reconnect was successful */ + NETWORK_RECONNECTED = 3, + /** Returned when a read attempt is made on the TLS buffer and it is empty */ + MQTT_NOTHING_TO_READ = 2, + /** Returned when a connection request is successful and packet response is connection accepted */ + MQTT_CONNACK_CONNECTION_ACCEPTED = 1, + /** Success return value - no error occurred */ + AWS_SUCCESS = 0, + /** A generic error. Not enough information for a specific error code */ + FAILURE = -1, + /** A required parameter was passed as null */ + NULL_VALUE_ERROR = -2, + /** The TCP socket could not be established */ + TCP_CONNECTION_ERROR = -3, + /** The TLS handshake failed */ + SSL_CONNECTION_ERROR = -4, + /** Error associated with setting up the parameters of a Socket */ + TCP_SETUP_ERROR = -5, + /** A timeout occurred while waiting for the TLS handshake to complete. */ + NETWORK_SSL_CONNECT_TIMEOUT_ERROR = -6, + /** A Generic write error based on the platform used */ + NETWORK_SSL_WRITE_ERROR = -7, + /** SSL initialization error at the TLS layer */ + NETWORK_SSL_INIT_ERROR = -8, + /** An error occurred when loading the certificates. The certificates could not be located or are incorrectly formatted. */ + NETWORK_SSL_CERT_ERROR = -9, + /** SSL Write times out */ + NETWORK_SSL_WRITE_TIMEOUT_ERROR = -10, + /** SSL Read times out */ + NETWORK_SSL_READ_TIMEOUT_ERROR = -11, + /** A Generic error based on the platform used */ + NETWORK_SSL_READ_ERROR = -12, + /** Returned when the Network is disconnected and reconnect is either disabled or physical layer is disconnected */ + NETWORK_DISCONNECTED_ERROR = -13, + /** Returned when the Network is disconnected and the reconnect attempt has timed out */ + NETWORK_RECONNECT_TIMED_OUT_ERROR = -14, + /** Returned when the Network is already connected and a connection attempt is made */ + NETWORK_ALREADY_CONNECTED_ERROR = -15, + /** Network layer Error Codes */ + /** Network layer Random number generator seeding failed */ + NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED = -16, + /** A generic error code for Network layer errors */ + NETWORK_SSL_UNKNOWN_ERROR = -17, + /** Returned when the physical layer is disconnected */ + NETWORK_PHYSICAL_LAYER_DISCONNECTED = -18, + /** Returned when the root certificate is invalid */ + NETWORK_X509_ROOT_CRT_PARSE_ERROR = -19, + /** Returned when the device certificate is invalid */ + NETWORK_X509_DEVICE_CRT_PARSE_ERROR = -20, + /** Returned when the private key failed to parse */ + NETWORK_PK_PRIVATE_KEY_PARSE_ERROR = -21, + /** Returned when the network layer failed to open a socket */ + NETWORK_ERR_NET_SOCKET_FAILED = -22, + /** Returned when the server is unknown */ + NETWORK_ERR_NET_UNKNOWN_HOST = -23, + /** Returned when connect request failed */ + NETWORK_ERR_NET_CONNECT_FAILED = -24, + /** Returned when there is nothing to read in the TLS read buffer */ + NETWORK_SSL_NOTHING_TO_READ = -25, + /** A connection could not be established. */ + MQTT_CONNECTION_ERROR = -26, + /** A timeout occurred while waiting for the TLS handshake to complete */ + MQTT_CONNECT_TIMEOUT_ERROR = -27, + /** A timeout occurred while waiting for the TLS request complete */ + MQTT_REQUEST_TIMEOUT_ERROR = -28, + /** The current client state does not match the expected value */ + MQTT_UNEXPECTED_CLIENT_STATE_ERROR = -29, + /** The client state is not idle when request is being made */ + MQTT_CLIENT_NOT_IDLE_ERROR = -30, + /** The MQTT RX buffer received corrupt or unexpected message */ + MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR = -31, + /** The MQTT RX buffer received a bigger message. The message will be dropped */ + MQTT_RX_BUFFER_TOO_SHORT_ERROR = -32, + /** The MQTT TX buffer is too short for the outgoing message. Request will fail */ + MQTT_TX_BUFFER_TOO_SHORT_ERROR = -33, + /** The client is subscribed to the maximum possible number of subscriptions */ + MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR = -34, + /** Failed to decode the remaining packet length on incoming packet */ + MQTT_DECODE_REMAINING_LENGTH_ERROR = -35, + /** Connect request failed with the server returning an unknown error */ + MQTT_CONNACK_UNKNOWN_ERROR = -36, + /** Connect request failed with the server returning an unacceptable protocol version error */ + MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR = -37, + /** Connect request failed with the server returning an identifier rejected error */ + MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR = -38, + /** Connect request failed with the server returning an unavailable error */ + MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR = -39, + /** Connect request failed with the server returning a bad userdata error */ + MQTT_CONNACK_BAD_USERDATA_ERROR = -40, + /** Connect request failed with the server failing to authenticate the request */ + MQTT_CONNACK_NOT_AUTHORIZED_ERROR = -41, + /** An error occurred while parsing the JSON string. Usually malformed JSON. */ + JSON_PARSE_ERROR = -42, + /** Shadow: The response Ack table is currently full waiting for previously published updates */ + SHADOW_WAIT_FOR_PUBLISH = -43, + /** Any time an snprintf writes more than size value, this error will be returned */ + SHADOW_JSON_BUFFER_TRUNCATED = -44, + /** Any time an snprintf encounters an encoding error or not enough space in the given buffer */ + SHADOW_JSON_ERROR = -45, + /** Mutex initialization failed */ + MUTEX_INIT_ERROR = -46, + /** Mutex lock request failed */ + MUTEX_LOCK_ERROR = -47, + /** Mutex unlock request failed */ + MUTEX_UNLOCK_ERROR = -48, + /** Mutex destroy failed */ + MUTEX_DESTROY_ERROR = -49, + /** Input argument exceeded the allowed maximum size */ + MAX_SIZE_ERROR = -50, + /** Some limit has been exceeded, e.g. the maximum number of subscriptions has been reached */ + LIMIT_EXCEEDED_ERROR = -51, + /** Invalid input topic type */ + INVALID_TOPIC_TYPE_ERROR = -52 +} IoT_Error_t; + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_SDK_SRC_IOT_ERROR_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_jobs_interface.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,267 @@ +/* + * Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_jobs_interface.h + * @brief An interface for interacting with the AWS IoT Jobs system. + * + * This file defines utility functions for interacting with the AWS IoT jobs + * APIs over MQTT. It provides functions for managing subscriptions to job + * related topics and for sending queries and updates requests for jobs. + * Callers are responsible for managing the subscriptions and associating + * responses with the queries and update messages. + */ +#ifndef AWS_IOT_JOBS_INTERFACE_H_ +#define AWS_IOT_JOBS_INTERFACE_H_ + +#ifdef DISABLE_IOT_JOBS +#error "Jobs API is disabled" +#endif + +/** + * @file aws_iot_jobs_interface.h + * @brief Functions for interacting with the AWS IoT Jobs system. + */ +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_jobs_topics.h" +#include "aws_iot_jobs_types.h" +#include "aws_iot_error.h" +#include "aws_iot_json_utils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Subscribe to jobs messages for the given thing and/or jobs. + * + * The function can be used to subscribe to all job related messages. To subscribe + * to messages not related to a job the jobId passed should be null. The jobId + * can also be "+" to subscribe to messages related to any job, or "$next" to + * indicate the next pending job. + * + * See also #aws_iot_jobs_subscribe_to_all_job_messages to subscribe to all possible + * messages in one operation. + * + * \note Subscribing with the same thing, job and topic type more than + * once is undefined. + * + * \param pClient the client to use + * \param qos the qos to use + * \param thingName the name of the thing to subscribe to + * \param jobId the job id to subscribe to. To subscribe to messages not related to + * a job the jobId passed should be null. The jobId can also be "+" to subscribe to + * messages related to any job, or "$next" to indicate the next pending job. + * \param topicType the topic type to subscribe to + * \param replyType the reply topic type to subscribe to + * \param pApplicationHandler the callback handler + * \param pApplicationHandlerData the callback context data. This must remain valid at least until + * aws_iot_jobs_unsubscribe_from_job_messages is called. + * \param topicBuffer. A buffer to use to hold the topic name for the subscription. This buffer + * must remain valid at least until aws_iot_jobs_unsubscribe_from_job_messages is called. + * \param topicBufferSize the size of the topic buffer. The function will fail + * with LIMIT_EXCEEDED_ERROR if this is too small. + * \return the result of subscribing to the topic (see aws_iot_mqtt_subscribe) + */ +IoT_Error_t aws_iot_jobs_subscribe_to_job_messages( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const char *jobId, + AwsIotJobExecutionTopicType topicType, + AwsIotJobExecutionTopicReplyType replyType, + pApplicationHandler_t pApplicationHandler, + void *pApplicationHandlerData, + char *topicBuffer, + uint16_t topicBufferSize); + +/** + * @brief Subscribe to all job messages. + * + * Subscribe to all job messages for the given thing. + * + * \note Subscribing with the same thing more than once is undefined. + * + * \param pClient the client to use + * \param qos the qos to use + * \param thingName the name of the thing to subscribe to + * \param pApplicationHandler the callback handler + * \param pApplicationHandlerData the callback context data. This must remain valid at least until + * aws_iot_jobs_unsubscribe_from_job_messages is called. + * \param topicBuffer. A buffer to use to hold the topic name for the subscription. This buffer + * must remain valid at least until aws_iot_jobs_unsubscribe_from_job_messages is called. + * \param topicBufferSize the size of the topic buffer. The function will fail + * with LIMIT_EXCEEDED_ERROR if this is too small. + * \return the result of subscribing to the topic (see aws_iot_mqtt_subscribe) + */ +IoT_Error_t aws_iot_jobs_subscribe_to_all_job_messages( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + pApplicationHandler_t pApplicationHandler, + void *pApplicationHandlerData, + char *topicBuffer, + uint16_t topicBufferSize); + +/** + * @brief Unsubscribe from a job subscription + * + * Remove the subscription created using #aws_iot_jobs_subscribe_to_job_messages or + * #aws_iot_jobs_subscribe_to_all_job_messages. + * + * \param pClient the client to use + * \param topicBuffer the topic buffer passed to #aws_iot_jobs_subscribe_to_job_messages or + * #aws_iot_jobs_subscribe_to_all_job_messages when the subscription was created. + * \return #AWS_SUCCESS or the first error encountered. + */ +IoT_Error_t aws_iot_jobs_unsubscribe_from_job_messages( + AWS_IoT_Client *pClient, + char *topicBuffer); + +/** + * @brief Send a query to one of the job query APIs. + * + * Send a query to one of the job query APIs. If jobId is null this + * requests a list of pending jobs for the thing. If jobId is + * not null then it sends a query for the details of that job. + * If jobId is $next then it sends a query for the details for + * the next pending job. + * + * \param pClient the client to use + * \param qos the qos to use + * \param thingName the thing name to query for + * \param jobId the id of the job to query for. If null a list + * of all pending jobs for the thing is requested. + * \param clientToken the client token to use for the query. + * If null no clientToken is sent resulting in an empty message. + * \param topicBuffer the topic buffer to use. This may be discarded + * as soon as this function returns + * \param topicBufferSize the size of topicBuffer + * \param messageBuffer the message buffer to use. May be NULL + * if clientToken is NULL + * \param messageBufferSize the size of messageBuffer + * \param topicType the topic type to publish query to + * \return LIMIT_EXCEEDED_ERROR if the topic buffer or + * message buffer is too small, NULL_VALUE_ERROR if the any of + * the required inputs are NULL, otherwise the result + * of the mqtt publish + */ +IoT_Error_t aws_iot_jobs_send_query( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const char *jobId, + const char *clientToken, + char *topicBuffer, + uint16_t topicBufferSize, + char *messageBuffer, + size_t messageBufferSize, + AwsIotJobExecutionTopicType topicType); + +/** + * @brief Send a start next command to the job start-next API. + * + * Send a start next command to the job start-next API. + * + * \param pClient the client to use + * \param qos the qos to use + * \param thingName the thing name to query for + * \param startNextRequest the start-next request to send + * \param topicBuffer the topic buffer to use. This may be discarded + * as soon as this function returns + * \param topicBufferSize the size of topicBuffer + * \param messageBuffer the message buffer to use. May be NULL + * if clientToken is NULL + * \param messageBufferSize the size of messageBuffer + * \return LIMIT_EXCEEDED_ERROR if the topic buffer or + * message buffer is too small, NULL_VALUE_ERROR if the any of + * the required inputs are NULL, otherwise the result + * of the mqtt publish + */ +IoT_Error_t aws_iot_jobs_start_next( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const AwsIotStartNextPendingJobExecutionRequest *startNextRequest, + char *topicBuffer, + uint16_t topicBufferSize, + char *messageBuffer, + size_t messageBufferSize); + +/** + * @brief Send a describe job query to the job query API. + * + * Send a describe job query to the job query API. If jobId is null this + * requests a list of pending jobs for the thing. If jobId is + * not null then it sends a query for the details of that job. + * + * \param pClient the client to use + * \param qos the qos to use + * \param thingName the thing name to query for + * \param jobId the id of the job to query for. If null a list + * of all pending jobs for the thing is requested. + * \param describeRequest the describe request to send + * \param topicBuffer the topic buffer to use. This may be discarded + * as soon as this function returns + * \param topicBufferSize the size of topicBuffer + * \param messageBuffer the message buffer to use. May be NULL + * if clientToken is NULL + * \param messageBufferSize the size of messageBuffer + * \return LIMIT_EXCEEDED_ERROR if the topic buffer or + * message buffer is too small, NULL_VALUE_ERROR if the any of + * the required inputs are NULL, otherwise the result + * of the mqtt publish + */ +IoT_Error_t aws_iot_jobs_describe( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const char *jobId, + const AwsIotDescribeJobExecutionRequest *describeRequest, + char *topicBuffer, + uint16_t topicBufferSize, + char *messageBuffer, + size_t messageBufferSize); + +/** + * @brief Send an update about a job execution. + * + * Send an update about a job execution. + * + * \param pClient the client to use + * \param qos the qos to use + * \param thingName the thing name to send the update for + * \param jobId the id of the job to send the update for + * \param updateRequest the update request to send + * \param topicBuffer the topic buffer to use. This may be discarded + * as soon as this function returns + * \param topicBufferSize the size of topicBuffer + * \param messageBuffer the message buffer to use. + * \param messageBufferSize the size of messageBuffer + * \return LIMIT_EXCEEDED_ERROR if the topic buffer or + * message buffer is too small, NULL_VALUE_ERROR if the any of + * the required inputs are NULL, otherwise the result + * of the mqtt publish + */ +IoT_Error_t aws_iot_jobs_send_update( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const char *jobId, + const AwsIotJobExecutionUpdateRequest *updateRequest, + char *topicBuffer, + uint16_t topicBufferSize, + char *messageBuffer, + size_t messageBufferSize); + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_JOBS_INTERFACE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_jobs_json.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,108 @@ +/* + * Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_jobs_json.h + * @brief Functions for mapping between json and the AWS Iot Job data structures. + */ + +#ifdef DISABLE_IOT_JOBS +#error "Jobs API is disabled" +#endif + +#ifndef AWS_IOT_JOBS_JSON_H_ +#define AWS_IOT_JOBS_JSON_H_ + +#include <stdbool.h> +#include "jsmn.h" +#include "aws_iot_jobs_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Serialize a job execution update request into a json string. + * + * \param requestBuffer buffer to contain the serialized request. If null + * this function will return the size of the buffer required + * \param bufferSize the size of the buffer. If this is smaller than the required + * length the string will be truncated to fit. + * \request the request to serialize. + * \return The size of the json string to store the serialized request or -1 + * if the request is invalid. Note that the return value should be checked against + * the size of the buffer and if its larger handle the fact that the string has + * been truncated. + */ +int aws_iot_jobs_json_serialize_update_job_execution_request( + char *requestBuffer, size_t bufferSize, + const AwsIotJobExecutionUpdateRequest *request); + +/** + * Serialize a job API request that contains only a client token. + * + * \param requestBuffer buffer to contain the serialized request. If null + * this function will return the size of the buffer required + * \param bufferSize the size of the buffer. If this is smaller than the required + * length the string will be truncated to fit. + * \param clientToken the client token to use for the request. + * \return The size of the json string to store the serialized request or -1 + * if the request is invalid. Note that the return value should be checked against + * the size of the buffer and if its larger handle the fact that the string has + * been truncated. + */ +int aws_iot_jobs_json_serialize_client_token_only_request( + char *requestBuffer, size_t bufferSize, + const char *clientToken); + +/** + * Serialize describe job execution request into json string. + * + * \param requestBuffer buffer to contain the serialized request. If null + * this function will return the size of the buffer required + * \param bufferSize the size of the buffer. If this is smaller than the required + * length the string will be truncated to fit. + * \param request the request to serialize. + * \return The size of the json string to store the serialized request or -1 + * if the request is invalid. Note that the return value should be checked against + * the size of the buffer and if its larger handle the fact that the string has + * been truncated. + */ +int aws_iot_jobs_json_serialize_describe_job_execution_request( + char *requestBuffer, size_t bufferSize, + const AwsIotDescribeJobExecutionRequest *request); + +/** + * Serialize start next job execution request into json string. + * + * \param requestBuffer buffer to contain the serialized request. If null + * this function will return the size of the buffer required + * \param bufferSize the size of the buffer. If this is smaller than the required + * length the string will be truncated to fit. + * \param request the start-next request to serialize. + * \return The size of the json string to store the serialized request or -1 + * if the request is invalid. Note that the return value should be checked against + * the size of the buffer and if its larger handle the fact that the string has + * been truncated. + */ +int aws_iot_jobs_json_serialize_start_next_job_execution_request( + char *requestBuffer, size_t bufferSize, + const AwsIotStartNextPendingJobExecutionRequest *request); + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_JOBS_JSON_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_jobs_topics.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,88 @@ +/* + * Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_job_topics.h + * @brief Functions for parsing and creating MQTT topics used by the AWS IoT Jobs system. + */ + +#ifdef DISABLE_IOT_JOBS +#error "Jobs API is disabled" +#endif + +#ifndef AWS_IOT_JOBS_TOPICS_H_ +#define AWS_IOT_JOBS_TOPICS_H_ + +#include <stdint.h> +#include <stdbool.h> +#include <stddef.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define JOB_ID_NEXT "$next" +#define JOB_ID_WILDCARD "+" + +/** + * The type of job topic. + */ +typedef enum { + JOB_UNRECOGNIZED_TOPIC = 0, + JOB_GET_PENDING_TOPIC, + JOB_START_NEXT_TOPIC, + JOB_DESCRIBE_TOPIC, + JOB_UPDATE_TOPIC, + JOB_NOTIFY_TOPIC, + JOB_NOTIFY_NEXT_TOPIC, + JOB_WILDCARD_TOPIC +} AwsIotJobExecutionTopicType; + +/** + * The type of reply topic, or #JOB_REQUEST_TYPE for + * topics that are not replies. + */ +typedef enum { + JOB_UNRECOGNIZED_TOPIC_TYPE = 0, + JOB_REQUEST_TYPE, + JOB_ACCEPTED_REPLY_TYPE, + JOB_REJECTED_REPLY_TYPE, + JOB_WILDCARD_REPLY_TYPE +} AwsIotJobExecutionTopicReplyType; + +/** + * @brief Get the topic matching the provided details and put into the provided buffer. + * + * If the buffer is not large enough to store the full topic the topic will be truncated + * to fit, with the last character always being a null terminator. + * + * \param buffer the buffer to put the results into + * \param bufferSize the size of the buffer + * \param topicType the type of the topic + * \param replyType the reply type of the topic + * \param thingName the name of the thing in the topic + * \param jobId the name of the job id in the topic + * \return the number of characters in the topic excluding the null terminator. A return + * value of bufferSize or more means that the topic string was truncated. + */ +int aws_iot_jobs_get_api_topic(char *buffer, size_t bufferSize, + AwsIotJobExecutionTopicType topicType, AwsIotJobExecutionTopicReplyType replyType, + const char* thingName, const char* jobId); + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_JOBS_TOPICS_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_jobs_types.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,109 @@ +/* + * Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_jobs_types.h + * @brief Structures defining the interface with the AWS IoT Jobs system + * + * This file defines the structures returned by and sent to the AWS IoT Jobs system. + * + */ + +#ifdef DISABLE_IOT_JOBS +#error "Jobs API is disabled" +#endif + +#ifndef AWS_IOT_JOBS_TYPES_H_ +#define AWS_IOT_JOBS_TYPES_H_ + +#include <stdbool.h> +#include <stdint.h> +#include "jsmn.h" +#include "timer_interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + JOB_EXECUTION_STATUS_NOT_SET = 0, + JOB_EXECUTION_QUEUED, + JOB_EXECUTION_IN_PROGRESS, + JOB_EXECUTION_FAILED, + JOB_EXECUTION_SUCCEEDED, + JOB_EXECUTION_CANCELED, + JOB_EXECUTION_REJECTED, + /*** + * Used for any status not in the supported list of statuses + */ + JOB_EXECUTION_UNKNOWN_STATUS = 99 +} JobExecutionStatus; + +extern const char *JOB_EXECUTION_QUEUED_STR; +extern const char *JOB_EXECUTION_IN_PROGRESS_STR; +extern const char *JOB_EXECUTION_FAILED_STR; +extern const char *JOB_EXECUTION_SUCCESS_STR; +extern const char *JOB_EXECUTION_CANCELED_STR; +extern const char *JOB_EXECUTION_REJECTED_STR; + +/** + * Convert a string to its matching status. + * + * \return the matching status, or JOB_EXECUTION_UNKNOWN_STATUS if the string was not recognized. + */ +JobExecutionStatus aws_iot_jobs_map_string_to_job_status(const char *str); + +/** + * Convert a status to its string. + * + * \return a string representing the status, or null if the status is not recognized. + */ +const char *aws_iot_jobs_map_status_to_string(JobExecutionStatus status); + +/** + * A request to update the status of a job execution. + */ +typedef struct { + int64_t expectedVersion; // set to 0 to ignore + int64_t executionNumber; // set to 0 to ignore + JobExecutionStatus status; + const char *statusDetails; + bool includeJobExecutionState; + bool includeJobDocument; + const char *clientToken; +} AwsIotJobExecutionUpdateRequest; + +/** + * A request to get the status of a job execution. + */ +typedef struct { + int64_t executionNumber; // set to 0 to ignore + bool includeJobDocument; + const char *clientToken; +} AwsIotDescribeJobExecutionRequest; + +/** + * A request to get and start the next pending (not in a terminal state) job execution for a Thing. + */ +typedef struct { + const char *statusDetails; + const char *clientToken; +} AwsIotStartNextPendingJobExecutionRequest; + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_JOBS_TYPES_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_json_utils.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,212 @@ +/* + * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_json_utils.h + * @brief Utilities for manipulating JSON + * + * json_utils provides JSON parsing utilities for use with the IoT SDK. + * Underlying JSON parsing relies on the Jasmine JSON parser. + * + */ + +#ifndef AWS_IOT_SDK_SRC_JSON_UTILS_H_ +#define AWS_IOT_SDK_SRC_JSON_UTILS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stdint.h> + +#include "aws_iot_error.h" +#include "jsmn.h" + +// utility functions +/** + * @brief JSON Equality Check + * + * Given a token pointing to a particular JSON node and an + * input string, check to see if the key is equal to the string. + * + * @param json json string + * @param tok json token - pointer to key to test for equality + * @param s input string for key to test equality + * + * @return 0 if equal, 1 otherwise + */ +int8_t jsoneq(const char *json, jsmntok_t *tok, const char *s); + +/** + * @brief Parse a signed 32-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of int32_t to be updated + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseInteger32Value(int32_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a signed 16-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of int16_t to be updated + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseInteger16Value(int16_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a signed 8-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of int8_t to be updated + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseInteger8Value(int8_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse an unsigned 32-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of uint32_t to be updated + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseUnsignedInteger32Value(uint32_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse an unsigned 16-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of uint16_t to be updated + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseUnsignedInteger16Value(uint16_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse an unsigned 8-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of uint8_t to be updated + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseUnsignedInteger8Value(uint8_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a float value from a JSON node. + * + * Given a JSON node parse the float value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param f address of float to be updated + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseFloatValue(float *f, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a double value from a JSON node. + * + * Given a JSON node parse the double value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param d address of double to be updated + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseDoubleValue(double *d, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a boolean value from a JSON node. + * + * Given a JSON node parse the boolean value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param b address of boolean to be updated + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseBooleanValue(bool *b, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a string value from a JSON node. + * + * Given a JSON node parse the string value from the value. + * + * @param buf address of string to be updated + * @param bufLen length of buf in bytes + * @param jsonString json string + * @param token json token - pointer to JSON node + * + * @return AWS_SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseStringValue(char *buf, size_t bufLen, const char *jsonString, jsmntok_t *token); + +/** + * @brief Find the JSON node associated with the given key in the given object. + * + * Given a JSON node parse the string value from the value. + * + * @param key json key + * @param token json token - pointer to JSON node + * @param jsonString json string + * + * @return pointer to found property value + * @return NULL - not found + */ +jsmntok_t *findToken(const char *key, const char *jsonString, jsmntok_t *token); + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_SDK_SRC_JSON_UTILS_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_log.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,131 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_log.h + * @brief Logging macros for the SDK. + * This file defines common logging macros with log levels to be used within the SDK. + * These macros can also be used in the IoT application code as a common way to output + * logs. The log levels can be tuned by modifying the makefile. Removing (commenting + * out) the IOT_* statement in the makefile disables that log level. + * + * It is expected that the macros below will be modified or replaced when porting to + * specific hardware platforms as printf may not be the desired behavior. + */ + +#ifndef _IOT_LOG_H +#define _IOT_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> +#include <stdlib.h> + +/** + * @brief Debug level logging macro. + * + * Macro to expose function, line number as well as desired log message. + */ +#ifdef ENABLE_IOT_DEBUG +#define IOT_DEBUG(...) \ + {\ + printf("DEBUG: %s L#%d ", __func__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } +#else +#define IOT_DEBUG(...) +#endif + +/** + * @brief Debug level trace logging macro. + * + * Macro to print message function entry and exit + */ +#ifdef ENABLE_IOT_TRACE +#define FUNC_ENTRY \ + {\ + printf("FUNC_ENTRY: %s L#%d (%s)\n", __func__, __LINE__, __FILE__); \ + } +#define FUNC_EXIT \ + {\ + printf("FUNC_EXIT: %s L#%d (%s)\n", __func__, __LINE__, __FILE__); \ + } +#define FUNC_EXIT_RC(x) \ + {\ + printf("FUNC_EXIT: %s L#%d Return Code : %d (%s)\n", __func__, __LINE__, x, __FILE__); \ + return x; \ + } +#else +#define FUNC_ENTRY + +#define FUNC_EXIT {return;} +#define FUNC_EXIT_RC(x) { return x; } +#endif + +/** + * @brief Info level logging macro. + * + * Macro to expose desired log message. Info messages do not include automatic function names and line numbers. + */ +#ifdef ENABLE_IOT_INFO +#define IOT_INFO(...) \ + {\ + printf(__VA_ARGS__); \ + printf("\n"); \ + } +#else +#define IOT_INFO(...) +#endif + +/** + * @brief Warn level logging macro. + * + * Macro to expose function, line number as well as desired log message. + */ +#ifdef ENABLE_IOT_WARN +#define IOT_WARN(...) \ + { \ + printf("WARN: %s L#%d ", __func__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } +#else +#define IOT_WARN(...) +#endif + +/** + * @brief Error level logging macro. + * + * Macro to expose function, line number as well as desired log message. + */ +#ifdef ENABLE_IOT_ERROR +#define IOT_ERROR(...) \ + { \ + printf("ERROR: %s L#%d ", __func__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } +#else +#define IOT_ERROR(...) +#endif + +#ifdef __cplusplus +} +#endif + +#endif // _IOT_LOG_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_mqtt_client.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,418 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client.h + * @brief Client definition for MQTT + */ + +#ifndef AWS_IOT_SDK_SRC_IOT_MQTT_CLIENT_H +#define AWS_IOT_SDK_SRC_IOT_MQTT_CLIENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Library Header files */ +#include "stdio.h" +#include "stdbool.h" +#include "stdint.h" +#include "stddef.h" + +/* AWS Specific header files */ +#include "aws_iot_error.h" +#include "aws_iot_config.h" + +/* Platform specific implementation header files */ +#include "network_interface.h" +#include "timer_interface.h" + +#ifdef _ENABLE_THREAD_SUPPORT_ +#include "threads_interface.h" +#endif + +#define MAX_PACKET_ID 65535 + +typedef struct _Client AWS_IoT_Client; + +/** + * @brief Quality of Service Type + * + * Defining a QoS type. + * @note QoS 2 is \b NOT supported by the AWS IoT Service at the time of this SDK release. + * + */ +typedef enum QoS { + QOS0 = 0, + QOS1 = 1 +} QoS; + +/** + * @brief Publish Message Parameters Type + * + * Defines a type for MQTT Publish messages. Used for both incoming and out going messages + * + */ +typedef struct { + QoS qos; ///< Message Quality of Service + uint8_t isRetained; ///< Retained messages are \b NOT supported by the AWS IoT Service at the time of this SDK release. + uint8_t isDup; ///< Is this message a duplicate QoS > 0 message? Handled automatically by the MQTT client. + uint16_t id; ///< Message sequence identifier. Handled automatically by the MQTT client. + void *payload; ///< Pointer to MQTT message payload (bytes). + size_t payloadLen; ///< Length of MQTT payload. +} IoT_Publish_Message_Params; + +/** + * @brief MQTT Version Type + * + * Defining an MQTT version type. Only 3.1.1 is supported at this time + * + */ +typedef enum { + MQTT_3_1_1 = 4 ///< MQTT 3.1.1 (protocol message byte = 4) +} MQTT_Ver_t; + +/** + * @brief Last Will and Testament Definition + * + * Defining a type for the MQTT "Last Will and Testament" (LWT) parameters. + * @note Retained messages are \b NOT supported by the AWS IoT Service at the time of this SDK release. + * + */ +typedef struct { + char struct_id[4]; ///< The eyecatcher for this structure. must be MQTW + char *pTopicName; ///< The LWT topic to which the LWT message will be published + uint16_t topicNameLen; ///< The length of the LWT topic, 16 bit unsinged integer + char *pMessage; ///< Message to be delivered as LWT + uint16_t msgLen; ///< The length of the Message, 16 bit unsinged integer + bool isRetained; ///< NOT supported. The retained flag for the LWT message (see MQTTAsync_message.retained) + QoS qos; ///< QoS of LWT message +} IoT_MQTT_Will_Options; +extern const IoT_MQTT_Will_Options iotMqttWillOptionsDefault; + +#define IoT_MQTT_Will_Options_Initializer { {'M', 'Q', 'T', 'W'}, NULL, 0, NULL, 0, false, QOS0 } + +/** + * @brief MQTT Connection Parameters + * + * Defining a type for MQTT connection parameters. Passed into client when establishing a connection. + * + */ +typedef struct { + char struct_id[4]; ///< The eyecatcher for this structure. must be MQTC + MQTT_Ver_t MQTTVersion; ///< Desired MQTT version used during connection + char *pClientID; ///< Pointer to a string defining the MQTT client ID (this needs to be unique \b per \b device across your AWS account) + uint16_t clientIDLen; ///< Client Id Length. 16 bit unsigned integer + uint16_t keepAliveIntervalInSec; ///< MQTT keep alive interval in seconds. Defines inactivity time allowed before determining the connection has been lost. + bool isCleanSession; ///< MQTT clean session. True = this session is to be treated as clean. Previous server state is cleared and no stated is retained from this connection. + bool isWillMsgPresent; ///< Is there a LWT associated with this connection? + IoT_MQTT_Will_Options will; ///< MQTT LWT parameters. + char *pUsername; ///< Not used in the AWS IoT Service, will need to be cstring if used + uint16_t usernameLen; ///< Username Length. 16 bit unsigned integer + char *pPassword; ///< Not used in the AWS IoT Service, will need to be cstring if used + uint16_t passwordLen; ///< Password Length. 16 bit unsigned integer +} IoT_Client_Connect_Params; +extern const IoT_Client_Connect_Params iotClientConnectParamsDefault; + +#define IoT_Client_Connect_Params_initializer { {'M', 'Q', 'T', 'C'}, MQTT_3_1_1, NULL, 0, 60, true, false, \ + IoT_MQTT_Will_Options_Initializer, NULL, 0, NULL, 0 } + +/** + * @brief Disconnect Callback Handler Type + * + * Defining a TYPE for definition of disconnect callback function pointers. + * + */ +typedef void (*iot_disconnect_handler)(AWS_IoT_Client *, void *); + +/** + * @brief MQTT Initialization Parameters + * + * Defining a type for MQTT initialization parameters. + * Passed into client when to initialize the client + * + */ +typedef struct { + bool enableAutoReconnect; ///< Set to true to enable auto reconnect + char *pHostURL; ///< Pointer to a string defining the endpoint for the MQTT service + uint16_t port; ///< MQTT service listening port + char *pRootCALocation; ///< Pointer to a string defining the Root CA file (full file, not path) + char *pDeviceCertLocation; ///< Pointer to a string defining the device identity certificate file (full file, not path) + char *pDevicePrivateKeyLocation; ///< Pointer to a string defining the device private key file (full file, not path) + uint32_t mqttPacketTimeout_ms; ///< Timeout for reading a complete MQTT packet. In milliseconds + uint32_t mqttCommandTimeout_ms; ///< Timeout for MQTT blocking calls. In milliseconds + uint32_t tlsHandshakeTimeout_ms; ///< TLS handshake timeout. In milliseconds + bool isSSLHostnameVerify; ///< Client should perform server certificate hostname validation + iot_disconnect_handler disconnectHandler; ///< Callback to be invoked upon connection loss + void *disconnectHandlerData; ///< Data to pass as argument when disconnect handler is called +#ifdef _ENABLE_THREAD_SUPPORT_ + bool isBlockOnThreadLockEnabled; ///< Timeout for Thread blocking calls. Set to 0 to block until lock is obtained. In milliseconds +#endif +} IoT_Client_Init_Params; +extern const IoT_Client_Init_Params iotClientInitParamsDefault; + +#ifdef _ENABLE_THREAD_SUPPORT_ +#define IoT_Client_Init_Params_initializer { true, NULL, 0, NULL, NULL, NULL, 2000, 20000, 5000, true, NULL, NULL, false } +#else +#define IoT_Client_Init_Params_initializer { true, NULL, 0, NULL, NULL, NULL, 2000, 20000, 5000, true, NULL, NULL } +#endif + +/** + * @brief MQTT Client State Type + * + * Defining a type for MQTT Client State + * + */ +typedef enum _ClientState { + CLIENT_STATE_INVALID = 0, + CLIENT_STATE_INITIALIZED = 1, + CLIENT_STATE_CONNECTING = 2, + CLIENT_STATE_CONNECTED_IDLE = 3, + CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS = 4, + CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS = 5, + CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS = 6, + CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS = 7, + CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS = 8, + CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN = 9, + CLIENT_STATE_DISCONNECTING = 10, + CLIENT_STATE_DISCONNECTED_ERROR = 11, + CLIENT_STATE_DISCONNECTED_MANUALLY = 12, + CLIENT_STATE_PENDING_RECONNECT = 13 +} ClientState; + +/** + * @brief Application Callback Handler Type + * + * Defining a TYPE for definition of application callback function pointers. + * Used to send incoming data to the application + * + */ +typedef void (*pApplicationHandler_t)(AWS_IoT_Client *pClient, char *pTopicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *pParams, void *pClientData); + +/** + * @brief MQTT Message Handler + * + * Defining a type for MQTT Message Handlers. + * Used to pass incoming data back to the application + * + */ +typedef struct _MessageHandlers { + const char *topicName; + uint16_t topicNameLen; + QoS qos; + pApplicationHandler_t pApplicationHandler; + void *pApplicationHandlerData; +} MessageHandlers; /* Message handlers are indexed by subscription topic */ + +/** + * @brief MQTT Client Status + * + * Defining a type for MQTT Client Status + * Contains information about the state of the MQTT Client + * + */ +typedef struct _ClientStatus { + ClientState clientState; + bool isPingOutstanding; + bool isAutoReconnectEnabled; +} ClientStatus; + +/** + * @brief MQTT Client Data + * + * Defining a type for MQTT Client Data + * Contains data used by the MQTT Client + * + */ +typedef struct _ClientData { + uint16_t nextPacketId; + + uint32_t packetTimeoutMs; + uint32_t commandTimeoutMs; + uint16_t keepAliveInterval; + uint32_t currentReconnectWaitInterval; + uint32_t counterNetworkDisconnected; + + /* The below values are initialized with the + * lengths of the TX/RX buffers and never modified + * afterwards */ + size_t writeBufSize; + size_t readBufSize; + + unsigned char writeBuf[AWS_IOT_MQTT_TX_BUF_LEN]; + unsigned char readBuf[AWS_IOT_MQTT_RX_BUF_LEN]; + +#ifdef _ENABLE_THREAD_SUPPORT_ + bool isBlockOnThreadLockEnabled; + IoT_Mutex_t state_change_mutex; + IoT_Mutex_t tls_read_mutex; + IoT_Mutex_t tls_write_mutex; +#endif + + IoT_Client_Connect_Params options; + + MessageHandlers messageHandlers[AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS]; + iot_disconnect_handler disconnectHandler; + + void *disconnectHandlerData; +} ClientData; + +/** + * @brief MQTT Client + * + * Defining a type for MQTT Client + * + */ +struct _Client { + awsTimer pingTimer; + awsTimer reconnectDelayTimer; + + ClientStatus clientStatus; + ClientData clientData; + Network networkStack; +}; + +/** + * @brief What is the next available packet Id + * + * Called to retrieve the next packet id to be used for outgoing packets. + * Automatically increments the last sent packet id variable + * + * @param pClient Reference to the IoT Client + * + * @return next packet id as a 16 bit unsigned integer + */ +uint16_t aws_iot_mqtt_get_next_packet_id(AWS_IoT_Client *pClient); + +/** + * @brief Set the connection parameters for the IoT Client + * + * Called to set the connection parameters for the IoT Client. + * Used to update the connection parameters provided before the last connect. + * Won't take effect until the next time connect is called + * + * @param pClient Reference to the IoT Client + * @param pNewConnectParams Reference to the new Connection Parameters structure + * + * @return IoT_Error_t Type defining successful/failed API call + */ +IoT_Error_t aws_iot_mqtt_set_connect_params(AWS_IoT_Client *pClient, IoT_Client_Connect_Params *pNewConnectParams); + +/** + * @brief Is the MQTT client currently connected? + * + * Called to determine if the MQTT client is currently connected. Used to support logic + * in the device application around reconnecting and managing offline state. + * + * @param pClient Reference to the IoT Client + * + * @return true = connected, false = not currently connected + */ +bool aws_iot_mqtt_is_client_connected(AWS_IoT_Client *pClient); + +/** + * @brief Get the current state of the client + * + * Called to get the current state of the client + * + * @param pClient Reference to the IoT Client + * + * @return ClientState value equal to the current state of the client + */ +ClientState aws_iot_mqtt_get_client_state(AWS_IoT_Client *pClient); + +/** + * @brief Is the MQTT client set to reconnect automatically? + * + * Called to determine if the MQTT client is set to reconnect automatically. + * Used to support logic in the device application around reconnecting + * + * @param pClient Reference to the IoT Client + * + * @return true = enabled, false = disabled + */ +bool aws_iot_is_autoreconnect_enabled(AWS_IoT_Client *pClient); + +/** + * @brief Set the IoT Client disconnect handler + * + * Called to set the IoT Client disconnect handler + * The disconnect handler is called whenever the client disconnects with error + * + * @param pClient Reference to the IoT Client + * @param pConnectHandler Reference to the new Disconnect Handler + * @param pDisconnectHandlerData Reference to the data to be passed as argument when disconnect handler is called + * + * @return IoT_Error_t Type defining successful/failed API call + */ +IoT_Error_t aws_iot_mqtt_set_disconnect_handler(AWS_IoT_Client *pClient, iot_disconnect_handler pDisconnectHandler, + void *pDisconnectHandlerData); + +/** + * @brief Enable or Disable AutoReconnect on Network Disconnect + * + * Called to enable or disabled the auto reconnect features provided with the SDK + * + * @param pClient Reference to the IoT Client + * @param newStatus set to true for enabling and false for disabling + * + * @return IoT_Error_t Type defining successful/failed API call + */ +IoT_Error_t aws_iot_mqtt_autoreconnect_set_status(AWS_IoT_Client *pClient, bool newStatus); + +/** + * @brief Get count of Network Disconnects + * + * Called to get the number of times a network disconnect occurred due to errors + * + * @param pClient Reference to the IoT Client + * + * @return uint32_t the disconnect count + */ +uint32_t aws_iot_mqtt_get_network_disconnected_count(AWS_IoT_Client *pClient); + +/** + * @brief Reset Network Disconnect conter + * + * Called to reset the Network Disconnect counter to zero + * + * @param pClient Reference to the IoT Client + */ +void aws_iot_mqtt_reset_network_disconnected_count(AWS_IoT_Client *pClient); + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_mqtt_client_common_internal.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,133 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_common_internal.h + * @brief Internal MQTT functions not exposed to application + */ + +#ifndef AWS_IOT_SDK_SRC_IOT_COMMON_INTERNAL_H +#define AWS_IOT_SDK_SRC_IOT_COMMON_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stddef.h> +#include <string.h> + +#include "aws_iot_log.h" +#include "aws_iot_mqtt_client_interface.h" + +/* Enum order should match the packet ids array defined in MQTTFormat.c */ +typedef enum msgTypes { + UNKNOWN = -1, + CONNECT = 1, + CONNACK = 2, + PUBLISH = 3, + PUBACK = 4, + PUBREC = 5, + PUBREL = 6, + PUBCOMP = 7, + SUBSCRIBE = 8, + SUBACK = 9, + UNSUBSCRIBE = 10, + UNSUBACK = 11, + PINGREQ = 12, + PINGRESP = 13, + DISCONNECT = 14 +} MessageTypes; + +/* Macros for parsing header fields from incoming MQTT frame. */ +#define MQTT_HEADER_FIELD_TYPE(_byte) ((_byte >> 4) & 0x0F) +#define MQTT_HEADER_FIELD_DUP(_byte) ((_byte & (1 << 3)) >> 3) +#define MQTT_HEADER_FIELD_QOS(_byte) ((_byte & (3 << 1)) >> 1) +#define MQTT_HEADER_FIELD_RETAIN(_byte) ((_byte & (1 << 0)) >> 0) + +/** + * Bitfields for the MQTT header byte. + */ +typedef union { + unsigned char byte; /**< the whole byte */ +} MQTTHeader; + +IoT_Error_t aws_iot_mqtt_internal_init_header(MQTTHeader *pHeader, MessageTypes message_type, + QoS qos, uint8_t dup, uint8_t retained); + +IoT_Error_t aws_iot_mqtt_internal_serialize_ack(unsigned char *pTxBuf, size_t txBufLen, + MessageTypes msgType, uint8_t dup, uint16_t packetId, + uint32_t *pSerializedLen); +IoT_Error_t aws_iot_mqtt_internal_deserialize_ack(unsigned char *, unsigned char *, + uint16_t *, unsigned char *, size_t); + +uint32_t aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(uint32_t rem_len); + +size_t aws_iot_mqtt_internal_write_len_to_buffer(unsigned char *buf, uint32_t length); +IoT_Error_t aws_iot_mqtt_internal_decode_remaining_length_from_buffer(unsigned char *buf, uint32_t *decodedLen, + uint32_t *readBytesLen); + +uint16_t aws_iot_mqtt_internal_read_uint16_t(unsigned char **pptr); +void aws_iot_mqtt_internal_write_uint_16(unsigned char **pptr, uint16_t anInt); + +unsigned char aws_iot_mqtt_internal_read_char(unsigned char **pptr); +void aws_iot_mqtt_internal_write_char(unsigned char **pptr, unsigned char c); +void aws_iot_mqtt_internal_write_utf8_string(unsigned char **pptr, const char *string, uint16_t stringLen); + +IoT_Error_t aws_iot_mqtt_internal_send_packet(AWS_IoT_Client *pClient, size_t length, awsTimer *pTimer); +IoT_Error_t aws_iot_mqtt_internal_cycle_read(AWS_IoT_Client *pClient, awsTimer *pTimer, uint8_t *pPacketType); +IoT_Error_t aws_iot_mqtt_internal_wait_for_read(AWS_IoT_Client *pClient, uint8_t packetType, awsTimer *pTimer); +IoT_Error_t aws_iot_mqtt_internal_serialize_zero(unsigned char *pTxBuf, size_t txBufLen, + MessageTypes packetType, size_t *pSerializedLength); +IoT_Error_t aws_iot_mqtt_internal_deserialize_publish(uint8_t *dup, QoS *qos, + uint8_t *retained, uint16_t *pPacketId, + char **pTopicName, uint16_t *topicNameLen, + unsigned char **payload, size_t *payloadLen, + unsigned char *pRxBuf, size_t rxBufLen); + +IoT_Error_t aws_iot_mqtt_set_client_state(AWS_IoT_Client *pClient, ClientState expectedCurrentState, + ClientState newState); + +#ifdef _ENABLE_THREAD_SUPPORT_ + +IoT_Error_t aws_iot_mqtt_client_lock_mutex(AWS_IoT_Client *pClient, IoT_Mutex_t *pMutex); + +IoT_Error_t aws_iot_mqtt_client_unlock_mutex(AWS_IoT_Client *pClient, IoT_Mutex_t *pMutex); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_SDK_SRC_IOT_COMMON_INTERNAL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_mqtt_client_interface.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,213 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_interface.h + * @brief Interface definition for MQTT client. + */ + +#ifndef AWS_IOT_SDK_SRC_IOT_MQTT_INTERFACE_H +#define AWS_IOT_SDK_SRC_IOT_MQTT_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Library Header files */ +#include "stdio.h" +#include "stdbool.h" +#include "stdint.h" +#include "stddef.h" + +/* AWS Specific header files */ +#include "aws_iot_error.h" +#include "aws_iot_config.h" +#include "aws_iot_mqtt_client.h" + +/* Platform specific implementation header files */ +#include "network_interface.h" +#include "timer_interface.h" + + +/** +* @brief Clean mqtt client from all dynamic memory allocate +* +* This function will free up memory that was dynamically allocated for the client. +* +* @param pClient MQTT Client that was previously created by calling aws_iot_mqtt_init +* @return An IoT Error Type defining successful/failed freeing +*/ + IoT_Error_t aws_iot_mqtt_free( AWS_IoT_Client *pClient ); + +/** + * @brief MQTT Client Initialization Function + * + * Called to initialize the MQTT Client + * + * @param pClient Reference to the IoT Client + * @param pInitParams Pointer to MQTT connection parameters + * + * @return IoT_Error_t Type defining successful/failed API call + */ +IoT_Error_t aws_iot_mqtt_init(AWS_IoT_Client *pClient, IoT_Client_Init_Params *pInitParams); + +/** + * @brief MQTT Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * + * @param pClient Reference to the IoT Client + * @param pConnectParams Pointer to MQTT connection parameters + * + * @return An IoT Error Type defining successful/failed connection + */ +IoT_Error_t aws_iot_mqtt_connect(AWS_IoT_Client *pClient, IoT_Client_Connect_Params *pConnectParams); + +/** + * @brief Publish an MQTT message on a topic + * + * Called to publish an MQTT message on a topic. + * @note Call is blocking. In the case of a QoS 0 message the function returns + * after the message was successfully passed to the TLS layer. In the case of QoS 1 + * the function returns after the receipt of the PUBACK control packet. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * @param pParams Pointer to Publish Message parameters + * + * @return An IoT Error Type defining successful/failed publish + */ +IoT_Error_t aws_iot_mqtt_publish(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *pParams); + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * @warning pTopicName and pApplicationHandlerData need to be static in memory. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to. pTopicName needs to be static in memory since + * no malloc are performed by the SDK + * @param topicNameLen Length of the topic name + * @param pApplicationHandler_t Reference to the handler function for this subscription + * @param pApplicationHandlerData Point to data passed to the callback. + * pApplicationHandlerData also needs to be static in memory since no malloc are performed by the SDK + * + * @return An IoT Error Type defining successful/failed subscription + */ +IoT_Error_t aws_iot_mqtt_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen, + QoS qos, pApplicationHandler_t pApplicationHandler, void *pApplicationHandlerData); + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to resubscribe to the topics that the client has active subscriptions on. + * Internally called when autoreconnect is enabled + * + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed subscription + */ +IoT_Error_t aws_iot_mqtt_resubscribe(AWS_IoT_Client *pClient); + +/** + * @brief Unsubscribe to an MQTT topic. + * + * Called to send an unsubscribe message to the broker requesting removal of a subscription + * to an MQTT topic. + * @note Call is blocking. The call returns after the receipt of the UNSUBACK control packet. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * + * @return An IoT Error Type defining successful/failed unsubscribe call + */ +IoT_Error_t aws_iot_mqtt_unsubscribe(AWS_IoT_Client *pClient, const char *pTopicFilter, uint16_t topicFilterLen); + +/** + * @brief Disconnect an MQTT Connection + * + * Called to send a disconnect message to the broker. + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed send of the disconnect control packet. + */ +IoT_Error_t aws_iot_mqtt_disconnect(AWS_IoT_Client *pClient); + +/** + * @brief Yield to the MQTT client + * + * Called to yield the current thread to the underlying MQTT client. This time is used by + * the MQTT client to manage PING requests to monitor the health of the TCP connection as + * well as periodically check the socket receive buffer for subscribe messages. Yield() + * must be called at a rate faster than the keepalive interval. It must also be called + * at a rate faster than the incoming message rate as this is the only way the client receives + * processing time to manage incoming messages. + * + * @param pClient Reference to the IoT Client + * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. + * + * @return An IoT Error Type defining successful/failed client processing. + * If this call results in an error it is likely the MQTT connection has dropped. + * iot_is_mqtt_connected can be called to confirm. + */ +IoT_Error_t aws_iot_mqtt_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms); + +/** + * @brief MQTT Manual Re-Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * using parameters from the last time a connection was attempted + * Use after disconnect to start the reconnect process manually + * Makes only one reconnect attempt Sets the client state to + * pending reconnect in case of failure + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed connection + */ +IoT_Error_t aws_iot_mqtt_attempt_reconnect(AWS_IoT_Client *pClient); + +#ifdef __cplusplus +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_shadow_actions.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef SRC_SHADOW_AWS_IOT_SHADOW_ACTIONS_H_ +#define SRC_SHADOW_AWS_IOT_SHADOW_ACTIONS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_shadow_interface.h" + +IoT_Error_t aws_iot_shadow_internal_action(const char *pThingName, ShadowActions_t action, + const char *pJsonDocumentToBeSent, size_t jsonSize, fpActionCallback_t callback, + void *pCallbackContext, uint32_t timeout_seconds, bool isSticky); + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_SHADOW_AWS_IOT_SHADOW_ACTIONS_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_shadow_interface.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,305 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#ifndef AWS_IOT_SDK_SRC_IOT_SHADOW_H_ +#define AWS_IOT_SDK_SRC_IOT_SHADOW_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @file aws_iot_shadow_interface.h + * @brief Interface for thing shadow + * + * These are the functions and structs to manage/interact the Thing Shadow(in the cloud). + * This SDK will let you interact with your own thing shadow or any other shadow using its Thing Name. + * There are totally 3 actions a device can perform on the shadow - Get, Update and Delete. + * + * Currently the device should use MQTT/S underneath. In the future this will also support other protocols. As it supports MQTT, the shadow needs to connect and disconnect. + * It will also work on the pub/sub model. On performing any action, the acknowledgment will be received in either accepted or rejected. For Example: + * If we want to perform a GET on the thing shadow the following messages will be sent and received: + * 1. A MQTT Publish on the topic - $aws/things/{thingName}/shadow/get + * 2. Subscribe to MQTT topics - $aws/things/{thingName}/shadow/get/accepted and $aws/things/{thingName}/shadow/get/rejected. + * If the request was successful we will receive the things json document in the accepted topic. + * + * + */ +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_shadow_json_data.h" + +/*! + * @brief Shadow Initialization parameters + * + * As the Shadow SDK uses MQTT underneath, it could be connected and disconnected on events to save some battery. + * @note Always use the \c ShadowIniTParametersDefault to initialize this struct + * + * + * + */ +typedef struct { + char *pHost; ///< This will be unique to a customer and can be retrieved from the console + uint16_t port; ///< Network port for TCP/IP socket + char *pRootCA; ///< Location with the Filename of the Root CA + char *pClientCRT; ///< Location of Device certs signed by AWS IoT service + char *pClientKey; ///< Location of Device private key + bool enableAutoReconnect; ///< Set to true to enable auto reconnect + iot_disconnect_handler disconnectHandler; ///< Callback to be invoked upon connection loss. +} ShadowInitParameters_t; + +/*! + * @brief Shadow Connect parameters + * + * As the Shadow SDK uses MQTT underneath, it could be connected and disconnected on events to save some battery. + * @note Always use the \c ShadowConnectParametersDefault to initialize this struct + * + *d + * + */ +typedef struct { + char *pMyThingName; ///< Every device has a Thing Shadow and this is the placeholder for name + char *pMqttClientId; ///< Currently the Shadow uses MQTT to connect and it is important to ensure we have unique client id + uint16_t mqttClientIdLen; ///< Currently the Shadow uses MQTT to connect and it is important to ensure we have unique client id + pApplicationHandler_t deleteActionHandler; ///< Callback to be invoked when Thing shadow for this device is deleted +} ShadowConnectParameters_t; + +/*! + * @brief This is set to defaults from the configuration file + * The certs are set to NULL because they need the path to the file. shadow_sample.c file demonstrates on how to get the relative path + * + * \relates ShadowInitParameters_t + */ +extern const ShadowInitParameters_t ShadowInitParametersDefault; + +/*! + * @brief This is set to defaults from the configuration file + * The length of the client id is initialized as 0. This is due to C language limitations of using constant literals + * only for creating const variables. The client id will be assigned using the value from aws_iot_config.h but the + * length needs to be assigned in code. shadow_sample.c file demonstrates this. + * + * \relates ShadowConnectParameters_t + */ +extern const ShadowConnectParameters_t ShadowConnectParametersDefault; + +/** +* @brief Clean shadow client from all dynamic memory allocate +* +* This function will free up memory that was dynamically allocated for the client. +* +* @param pClient MQTT Client that was previously created by calling aws_iot_shadow_init +* @return An IoT Error Type defining successful/failed freeing +*/ +IoT_Error_t aws_iot_shadow_free(AWS_IoT_Client *pClient); + +/** + * @brief Initialize the Thing Shadow before use + * + * This function takes care of initializing the internal book-keeping data structures and initializing the IoT client. + * + * @param pClient A new MQTT Client to be used as the protocol layer. Will be initialized with pParams. + * @return An IoT Error Type defining successful/failed Initialization + */ +IoT_Error_t aws_iot_shadow_init(AWS_IoT_Client *pClient, ShadowInitParameters_t *pParams); + +/** + * @brief Connect to the AWS IoT Thing Shadow service over MQTT + * + * This function does the TLSv1.2 handshake and establishes the MQTT connection + * + * @param pClient MQTT Client used as the protocol layer + * @param pParams Shadow Conenction parameters like TLS cert location + * @return An IoT Error Type defining successful/failed Connection + */ +IoT_Error_t aws_iot_shadow_connect(AWS_IoT_Client *pClient, ShadowConnectParameters_t *pParams); + +/** + * @brief Yield function to let the background tasks of MQTT and Shadow + * + * This function could be use in a separate thread waiting for the incoming messages, ensuring the connection is kept alive with the AWS Service. + * It also ensures the expired requests of Shadow actions are cleared and Timeout callback is executed. + * @note All callbacks ever used in the SDK will be executed in the context of this function. + * + * @param pClient MQTT Client used as the protocol layer + * @param timeout in milliseconds, This is the maximum time the yield function will wait for a message and/or read the messages from the TLS buffer + * @return An IoT Error Type defining successful/failed Yield + */ +IoT_Error_t aws_iot_shadow_yield(AWS_IoT_Client *pClient, uint32_t timeout); + +/** + * @brief Disconnect from the AWS IoT Thing Shadow service over MQTT + * + * This will close the underlying TCP connection, MQTT connection will also be closed + * + * @param pClient MQTT Client used as the protocol layer + * @return An IoT Error Type defining successful/failed disconnect status + */ +IoT_Error_t aws_iot_shadow_disconnect(AWS_IoT_Client *pClient); + +/** + * @brief Thing Shadow Acknowledgment enum + * + * This enum type is use in the callback for the action response + * + */ +typedef enum { + SHADOW_ACK_TIMEOUT, SHADOW_ACK_REJECTED, SHADOW_ACK_ACCEPTED +} Shadow_Ack_Status_t; + +/** + * @brief Thing Shadow Action type enum + * + * This enum type is use in the callback for the action response + * + */ +typedef enum { + SHADOW_GET, SHADOW_UPDATE, SHADOW_DELETE +} ShadowActions_t; + + +/** + * @brief Function Pointer typedef used as the callback for every action + * + * This function will be called from the context of \c aws_iot_shadow_yield() context + * + * @param pThingName Thing Name of the response received + * @param action The response of the action + * @param status Informs if the action was Accepted/Rejected or Timed out + * @param pReceivedJsonDocument Received JSON document + * @param pContextData the void* data passed in during the action call(update, get or delete) + * + */ +typedef void (*fpActionCallback_t)(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status, + const char *pReceivedJsonDocument, void *pContextData); + +/** + * @brief This function is the one used to perform an Update action to a Thing Name's Shadow. + * + * update is one of the most frequently used functionality by a device. In most cases the device may be just reporting few params to update the thing shadow in the cloud + * Update Action if no callback or if the JSON document does not have a client token then will just publish the update and not track it. + * + * @note The update has to subscribe to two topics update/accepted and update/rejected. This function waits 2 seconds to ensure the subscriptions are registered before publishing the update message. + * The following steps are performed on using this function: + * 1. Subscribe to Shadow topics - $aws/things/{thingName}/shadow/update/accepted and $aws/things/{thingName}/shadow/update/rejected + * 2. wait for 2 seconds for the subscription to take effect + * 3. Publish on the update topic - $aws/things/{thingName}/shadow/update + * 4. In the \c aws_iot_shadow_yield() function the response will be handled. In case of timeout or if the response is received, the subscription to shadow response topics are un-subscribed from. + * On the contrary if the persistent subscription is set to true then the un-subscribe will not be done. The topics will always be listened to. + * + * @param pClient MQTT Client used as the protocol layer + * @param pThingName Thing Name of the shadow that needs to be Updated + * @param pJsonString The update action expects a JSON document to send. The JSON String should be a null terminated string. This JSON document should adhere to the AWS IoT Thing Shadow specification. To help in the process of creating this document- SDK provides apis in \c aws_iot_shadow_json_data.h + * @param callback This is the callback that will be used to inform the caller of the response from the AWS IoT Shadow service.Callback could be set to NULL if response is not important + * @param pContextData This is an extra parameter that could be passed along with the callback. It should be set to NULL if not used + * @param timeout_seconds It is the time the SDK will wait for the response on either accepted/rejected before declaring timeout on the action + * @param isPersistentSubscribe As mentioned above, every time if a device updates the same shadow then this should be set to true to avoid repeated subscription and unsubscription. If the Thing Name is one off update then this should be set to false + * @return An IoT Error Type defining successful/failed update action + */ +IoT_Error_t aws_iot_shadow_update(AWS_IoT_Client *pClient, const char *pThingName, char *pJsonString, + fpActionCallback_t callback, void *pContextData, uint8_t timeout_seconds, + bool isPersistentSubscribe); + +/** + * @brief This function is the one used to perform an Get action to a Thing Name's Shadow. + * + * One use of this function is usually to get the config of a device at boot up. + * It is similar to the Update function internally except it does not take a JSON document as the input. The entire JSON document will be sent over the accepted topic + * + * @param pClient MQTT Client used as the protocol layer + * @param pThingName Thing Name of the JSON document that is needed + * @param callback This is the callback that will be used to inform the caller of the response from the AWS IoT Shadow service.Callback could be set to NULL if response is not important + * @param pContextData This is an extra parameter that could be passed along with the callback. It should be set to NULL if not used + * @param timeout_seconds It is the time the SDK will wait for the response on either accepted/rejected before declaring timeout on the action + * @param isPersistentSubscribe As mentioned above, every time if a device gets the same Sahdow (JSON document) then this should be set to true to avoid repeated subscription and un-subscription. If the Thing Name is one off get then this should be set to false + * @return An IoT Error Type defining successful/failed get action + */ +IoT_Error_t aws_iot_shadow_get(AWS_IoT_Client *pClient, const char *pThingName, fpActionCallback_t callback, + void *pContextData, uint8_t timeout_seconds, bool isPersistentSubscribe); + +/** + * @brief This function is the one used to perform an Delete action to a Thing Name's Shadow. + * + * This is not a very common use case for device. It is generally the responsibility of the accompanying app to do the delete. + * It is similar to the Update function internally except it does not take a JSON document as the input. The Thing Shadow referred by the ThingName will be deleted. + * + * @param pClient MQTT Client used as the protocol layer + * @param pThingName Thing Name of the Shadow that should be deleted + * @param callback This is the callback that will be used to inform the caller of the response from the AWS IoT Shadow service.Callback could be set to NULL if response is not important + * @param pContextData This is an extra parameter that could be passed along with the callback. It should be set to NULL if not used + * @param timeout_seconds It is the time the SDK will wait for the response on either accepted/rejected before declaring timeout on the action + * @param isPersistentSubscribe As mentioned above, every time if a device deletes the same Shadow (JSON document) then this should be set to true to avoid repeated subscription and un-subscription. If the Thing Name is one off delete then this should be set to false + * @return An IoT Error Type defining successful/failed delete action + */ +IoT_Error_t aws_iot_shadow_delete(AWS_IoT_Client *pClient, const char *pThingName, fpActionCallback_t callback, + void *pContextData, uint8_t timeout_seconds, bool isPersistentSubscriptions); + +/** + * @brief This function is used to listen on the delta topic of #AWS_IOT_MY_THING_NAME mentioned in the aws_iot_config.h file. + * + * Any time a delta is published the Json document will be delivered to the pStruct->cb. If you don't want the parsing done by the SDK then use the jsonStruct_t key set to "state". A good example of this is displayed in the sample_apps/shadow_console_echo.c + * + * @param pClient MQTT Client used as the protocol layer + * @param pStruct The struct used to parse JSON value + * @return An IoT Error Type defining successful/failed delta registering + */ +IoT_Error_t aws_iot_shadow_register_delta(AWS_IoT_Client *pClient, jsonStruct_t *pStruct); + +/** + * @brief Reset the last received version number to zero. + * This will be useful if the Thing Shadow is deleted and would like to to reset the local version + * @return no return values + * + */ +void aws_iot_shadow_reset_last_received_version(void); + +/** + * @brief Version of a document is received with every accepted/rejected and the SDK keeps track of the last received version of the JSON document of #AWS_IOT_MY_THING_NAME shadow + * + * One exception to this version tracking is that, the SDK will ignore the version from update/accepted topic. Rest of the responses will be scanned to update the version number. + * Accepting version change for update/accepted may cause version conflicts for delta message if the update message is received before the delta. + * + * @return version number of the last received response + * + */ +uint32_t aws_iot_shadow_get_last_received_version(void); + +/** + * @brief Enable the ignoring of delta messages with old version number + * + * As we use MQTT underneath, there could be more than 1 of the same message if we use QoS 0. To avoid getting called for the same message, this functionality should be enabled. All the old message will be ignored + */ +void aws_iot_shadow_enable_discard_old_delta_msgs(void); + +/** + * @brief Disable the ignoring of delta messages with old version number + */ +void aws_iot_shadow_disable_discard_old_delta_msgs(void); + +/** + * @brief This function is used to enable or disable autoreconnect + * + * Any time a disconnect happens the underlying MQTT client attempts to reconnect if this is set to true + * + * @param pClient MQTT Client used as the protocol layer + * @param newStatus The new status to set the autoreconnect option to + * + * @return An IoT Error Type defining successful/failed operation + */ +IoT_Error_t aws_iot_shadow_set_autoreconnect_status(AWS_IoT_Client *pClient, bool newStatus); + +#ifdef __cplusplus +} +#endif + +#endif //AWS_IOT_SDK_SRC_IOT_SHADOW_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_shadow_json.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#ifndef AWS_IOT_SDK_SRC_IOT_SHADOW_JSON_H_ +#define AWS_IOT_SDK_SRC_IOT_SHADOW_JSON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdbool.h> +#include <stdarg.h> + +#include "aws_iot_error.h" +#include "aws_iot_shadow_json_data.h" + +bool isJsonValidAndParse(const char *pJsonDocument, size_t jsonSize, void *pJsonHandler, int32_t *pTokenCount); + +bool isJsonKeyMatchingAndUpdateValue(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount, + jsonStruct_t *pDataStruct, uint32_t *pDataLength, int32_t *pDataPosition); + +IoT_Error_t aws_iot_shadow_internal_get_request_json(char *pBuffer, size_t bufferSize); + +IoT_Error_t aws_iot_shadow_internal_delete_request_json(char *pBuffer, size_t bufferSize); + +void resetClientTokenSequenceNum(void); + + +bool isReceivedJsonValid(const char *pJsonDocument, size_t jsonSize); + +bool extractClientToken(const char *pJsonDocument, size_t jsonSize, char *pExtractedClientToken, size_t clientTokenSize); + +bool extractVersionNumber(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount, uint32_t *pVersionNumber); + +#ifdef __cplusplus +} +#endif + +#endif // AWS_IOT_SDK_SRC_IOT_SHADOW_JSON_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_shadow_json_data.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,150 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef SRC_SHADOW_AWS_IOT_SHADOW_JSON_DATA_H_ +#define SRC_SHADOW_AWS_IOT_SHADOW_JSON_DATA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file aws_iot_shadow_json_data.h + * @brief This file is the interface for all the Shadow related JSON functions. + */ + +#include <stddef.h> + +/** + * @brief This is a static JSON object that could be used in code + * + */ +typedef struct jsonStruct jsonStruct_t; + +/** + * @brief Every JSON name value can have a callback. The callback should follow this signature + */ +typedef void (*jsonStructCallback_t)(const char *pJsonValueBuffer, uint32_t valueLength, jsonStruct_t *pJsonStruct_t); + +/** + * @brief All the JSON object types enum + * + * JSON number types need to be split into proper integer / floating point data types and sizes on embedded platforms. + */ +typedef enum { + SHADOW_JSON_INT32, + SHADOW_JSON_INT16, + SHADOW_JSON_INT8, + SHADOW_JSON_UINT32, + SHADOW_JSON_UINT16, + SHADOW_JSON_UINT8, + SHADOW_JSON_FLOAT, + SHADOW_JSON_DOUBLE, + SHADOW_JSON_BOOL, + SHADOW_JSON_STRING, + SHADOW_JSON_OBJECT +} JsonPrimitiveType; + +/** + * @brief This is the struct form of a JSON Key value pair + */ +struct jsonStruct { + const char *pKey; ///< JSON key + void *pData; ///< pointer to the data (JSON value) + size_t dataLength; ///< Length (in bytes) of pData + JsonPrimitiveType type; ///< type of JSON + jsonStructCallback_t cb; ///< callback to be executed on receiving the Key value pair +}; + +/** + * @brief Initialize the JSON document with Shadow expected name/value + * + * This Function will fill the JSON Buffer with a null terminated string. Internally it uses snprintf + * This function should always be used First, followed by iot_shadow_add_reported and/or iot_shadow_add_desired. + * Always finish the call sequence with iot_finalize_json_document + * + * @note Ensure the size of the Buffer is enough to hold the entire JSON Document. + * + * + * @param pJsonDocument The JSON Document filled in this char buffer + * @param maxSizeOfJsonDocument maximum size of the pJsonDocument that can be used to fill the JSON document + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ +IoT_Error_t aws_iot_shadow_init_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument); + +/** + * @brief Add the reported section of the JSON document of jsonStruct_t + * + * This is a variadic function and please be careful with the usage. count is the number of jsonStruct_t types that you would like to add in the reported section + * This function will add "reported":{<all the values that needs to be added>} + * + * @note Ensure the size of the Buffer is enough to hold the reported section + the init section. Always use the same JSON document buffer used in the iot_shadow_init_json_document function. This function will accommodate the size of previous null terminated string, so pass teh max size of the buffer + * + * + * @param pJsonDocument The JSON Document filled in this char buffer + * @param maxSizeOfJsonDocument maximum size of the pJsonDocument that can be used to fill the JSON document + * @param count total number of arguments(jsonStruct_t object) passed in the arguments + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ +IoT_Error_t aws_iot_shadow_add_reported(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...); + +/** + * @brief Add the desired section of the JSON document of jsonStruct_t + * + * This is a variadic function and please be careful with the usage. count is the number of jsonStruct_t types that you would like to add in the reported section + * This function will add "desired":{<all the values that needs to be added>} + * + * @note Ensure the size of the Buffer is enough to hold the reported section + the init section. Always use the same JSON document buffer used in the iot_shadow_init_json_document function. This function will accommodate the size of previous null terminated string, so pass the max size of the buffer + * + * + * @param pJsonDocument The JSON Document filled in this char buffer + * @param maxSizeOfJsonDocument maximum size of the pJsonDocument that can be used to fill the JSON document + * @param count total number of arguments(jsonStruct_t object) passed in the arguments + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ +IoT_Error_t aws_iot_shadow_add_desired(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...); + +/** + * @brief Finalize the JSON document with Shadow expected client Token. + * + * This function will automatically increment the client token every time this function is called. + * + * @note Ensure the size of the Buffer is enough to hold the entire JSON Document. If the finalized section is not invoked then the JSON doucment will not be valid + * + * + * @param pJsonDocument The JSON Document filled in this char buffer + * @param maxSizeOfJsonDocument maximum size of the pJsonDocument that can be used to fill the JSON document + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ +IoT_Error_t aws_iot_finalize_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument); + +/** + * @brief Fill the given buffer with client token for tracking the Repsonse. + * + * This function will add the AWS_IOT_MQTT_CLIENT_ID with a sequence number. Every time this function is used the sequence number gets incremented + * + * + * @param pBufferToBeUpdatedWithClientToken buffer to be updated with the client token string + * @param maxSizeOfJsonDocument maximum size of the pBufferToBeUpdatedWithClientToken that can be used + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ + +IoT_Error_t aws_iot_fill_with_client_token(char *pBufferToBeUpdatedWithClientToken, size_t maxSizeOfJsonDocument); + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_SHADOW_AWS_IOT_SHADOW_JSON_DATA_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_shadow_key.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef SRC_SHADOW_AWS_IOT_SHADOW_KEY_H_ +#define SRC_SHADOW_AWS_IOT_SHADOW_KEY_H_ + +#define SHADOW_CLIENT_TOKEN_STRING "clientToken" +#define SHADOW_VERSION_STRING "version" + +#endif /* SRC_SHADOW_AWS_IOT_SHADOW_KEY_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_shadow_records.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef SRC_SHADOW_AWS_IOT_SHADOW_RECORDS_H_ +#define SRC_SHADOW_AWS_IOT_SHADOW_RECORDS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> + +#include "aws_iot_shadow_interface.h" +#include "aws_iot_config.h" + + +extern uint32_t shadowJsonVersionNum; +extern bool shadowDiscardOldDeltaFlag; + +extern char myThingName[MAX_SIZE_OF_THING_NAME]; +extern uint16_t myThingNameLen; +extern char mqttClientID[MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES]; +extern uint16_t mqttClientIDLen; + +void initializeRecords(AWS_IoT_Client *pClient); +bool isSubscriptionPresent(const char *pThingName, ShadowActions_t action); +IoT_Error_t subscribeToShadowActionAcks(const char *pThingName, ShadowActions_t action, bool isSticky); +void incrementSubscriptionCnt(const char *pThingName, ShadowActions_t action, bool isSticky); + +IoT_Error_t publishToShadowAction(const char *pThingName, ShadowActions_t action, const char *pJsonDocumentToBeSent); +void addToAckWaitList(uint8_t indexAckWaitList, const char *pThingName, ShadowActions_t action, + const char *pExtractedClientToken, fpActionCallback_t callback, void *pCallbackContext, + uint32_t timeout_seconds); +bool getNextFreeIndexOfAckWaitList(uint8_t *pIndex); +void HandleExpiredResponseCallbacks(void); +void initDeltaTokens(void); +IoT_Error_t registerJsonTokenOnDelta(jsonStruct_t *pStruct); + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_SHADOW_AWS_IOT_SHADOW_RECORDS_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/aws_iot_version.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_version.h + * @brief Constants defining the release version of the SDK. + * + * This file contains constants defining the release version of the SDK. + * This file is modified by AWS upon release of the SDK and should not be + * modified by the consumer of the SDK. The provided samples show example + * usage of these constants. + * + * Versioning of the SDK follows the MAJOR.MINOR.PATCH Semantic Versioning guidelines. + * @see http://semver.org/ + */ +#ifndef SRC_UTILS_AWS_IOT_VERSION_H_ +#define SRC_UTILS_AWS_IOT_VERSION_H_ + +/** + * @brief MAJOR version, incremented when incompatible API changes are made. + */ +#define VERSION_MAJOR 3 +/** + * @brief MINOR version when functionality is added in a backwards-compatible manner. + */ +#define VERSION_MINOR 0 +/** + * @brief PATCH version when backwards-compatible bug fixes are made. + */ +#define VERSION_PATCH 0 +/** + * @brief TAG is an (optional) tag appended to the version if a more descriptive verion is needed. + */ +#define VERSION_TAG "" + +#endif /* SRC_UTILS_AWS_IOT_VERSION_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/network_interface.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,167 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file network_interface.h + * @brief Network interface definition for MQTT client. + * + * Defines an interface to the TLS layer to be used by the MQTT client. + * Starting point for porting the SDK to the networking layer of a new platform. + */ + +#ifndef __NETWORK_INTERFACE_H_ +#define __NETWORK_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdbool.h> +#include <aws_iot_error.h> +#include "timer_interface.h" +#include "network_platform.h" + +/** + * @brief Network Type + * + * Defines a type for the network struct. See structure definition below. + */ +typedef struct Network Network; + +/** + * @brief TLS Connection Parameters + * + * Defines a type containing TLS specific parameters to be passed down to the + * TLS networking layer to create a TLS secured socket. + */ +typedef struct { + char *pRootCALocation; ///< Pointer to string containing the filename (including path) of the root CA file. + char *pDeviceCertLocation; ///< Pointer to string containing the filename (including path) of the device certificate. + char *pDevicePrivateKeyLocation; ///< Pointer to string containing the filename (including path) of the device private key file. + char *pDestinationURL; ///< Pointer to string containing the endpoint of the MQTT service. + uint16_t DestinationPort; ///< Integer defining the connection port of the MQTT service. + uint32_t timeout_ms; ///< Unsigned integer defining the TLS handshake timeout value in milliseconds. + bool ServerVerificationFlag; ///< Boolean. True = perform server certificate hostname validation. False = skip validation \b NOT recommended. +} TLSConnectParams; + +/** + * @brief Network Structure + * + * Structure for defining a network connection. + */ +struct Network { + IoT_Error_t (*connect)(Network *, TLSConnectParams *); + + IoT_Error_t (*read)(Network *, unsigned char *, size_t, awsTimer *, size_t *); ///< Function pointer pointing to the network function to read from the network + IoT_Error_t (*write)(Network *, unsigned char *, size_t, awsTimer *, size_t *); ///< Function pointer pointing to the network function to write to the network + IoT_Error_t (*disconnect)(Network *); ///< Function pointer pointing to the network function to disconnect from the network + IoT_Error_t (*isConnected)(Network *); ///< Function pointer pointing to the network function to check if TLS is connected + IoT_Error_t (*destroy)(Network *); ///< Function pointer pointing to the network function to destroy the network object + + TLSConnectParams tlsConnectParams; ///< TLSConnect params structure containing the common connection parameters + TLSDataParams tlsDataParams; ///< TLSData params structure containing the connection data parameters that are specific to the library being used +}; + +/** + * @brief Initialize the TLS implementation + * + * Perform any initialization required by the TLS layer. + * Connects the interface to implementation by setting up + * the network layer function pointers to platform implementations. + * + * @param pNetwork - Pointer to a Network struct defining the network interface. + * @param pRootCALocation - Path of the location of the Root CA + * @param pDeviceCertLocation - Path to the location of the Device Cert + * @param pDevicyPrivateKeyLocation - Path to the location of the device private key file + * @param pDestinationURL - The target endpoint to connect to + * @param DestinationPort - The port on the target to connect to + * @param timeout_ms - The value to use for timeout of operation + * @param ServerVerificationFlag - used to decide whether server verification is needed or not + * + * @return IoT_Error_t - successful initialization or TLS error + */ +IoT_Error_t iot_tls_init(Network *pNetwork, char *pRootCALocation, char *pDeviceCertLocation, + char *pDevicePrivateKeyLocation, char *pDestinationURL, + uint16_t DestinationPort, uint32_t timeout_ms, bool ServerVerificationFlag); + +/** + * @brief Create a TLS socket and open the connection + * + * Creates an open socket connection including TLS handshake. + * + * @param pNetwork - Pointer to a Network struct defining the network interface. + * @param TLSParams - TLSConnectParams defines the properties of the TLS connection. + * @return IoT_Error_t - successful connection or TLS error + */ +IoT_Error_t iot_tls_connect(Network *pNetwork, TLSConnectParams *TLSParams); + +/** + * @brief Write bytes to the network socket + * + * @param Network - Pointer to a Network struct defining the network interface. + * @param unsigned char pointer - buffer to write to socket + * @param integer - number of bytes to write + * @param awsTimer * - operation timer + * @return integer - number of bytes written or TLS error + * @return IoT_Error_t - successful write or TLS error code + */ +IoT_Error_t iot_tls_write(Network *, unsigned char *, size_t, awsTimer *, size_t *); + +/** + * @brief Read bytes from the network socket + * + * @param Network - Pointer to a Network struct defining the network interface. + * @param unsigned char pointer - pointer to buffer where read bytes should be copied + * @param size_t - number of bytes to read + * @param awsTimer * - operation timer + * @param size_t - pointer to store number of bytes read + * @return IoT_Error_t - successful read or TLS error code + */ +IoT_Error_t iot_tls_read(Network *, unsigned char *, size_t, awsTimer *, size_t *); + +/** + * @brief Disconnect from network socket + * + * @param Network - Pointer to a Network struct defining the network interface. + * @return IoT_Error_t - successful read or TLS error code + */ +IoT_Error_t iot_tls_disconnect(Network *pNetwork); + +/** + * @brief Perform any tear-down or cleanup of TLS layer + * + * Called to cleanup any resources required for the TLS layer. + * + * @param Network - Pointer to a Network struct defining the network interface + * @return IoT_Error_t - successful cleanup or TLS error code + */ +IoT_Error_t iot_tls_destroy(Network *pNetwork); + +/** + * @brief Check if TLS layer is still connected + * + * Called to check if the TLS layer is still connected or not. + * + * @param Network - Pointer to a Network struct defining the network interface + * @return IoT_Error_t - TLS error code indicating status of network physical layer connection + */ +IoT_Error_t iot_tls_is_connected(Network *pNetwork); + +#ifdef __cplusplus +} +#endif + +#endif //__NETWORK_INTERFACE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/threads_interface.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,108 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +/** + * @file threads_interface.h + * @brief Thread interface definition for MQTT client. + * + * Defines an interface that can be used by system components for multithreaded situations. + * Starting point for porting the SDK to the threading hardware layer of a new platform. + */ + +#include "aws_iot_config.h" + +#ifdef _ENABLE_THREAD_SUPPORT_ +#ifndef __THREADS_INTERFACE_H_ +#define __THREADS_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The platform specific timer header that defines the Timer struct + */ +#include "threads_platform.h" + +#include <aws_iot_error.h> + +/** + * @brief Mutex Type + * + * Forward declaration of a mutex struct. The definition of this struct is + * platform dependent. When porting to a new platform add this definition + * in "threads_platform.h". + * + */ +typedef struct _IoT_Mutex_t IoT_Mutex_t; + +/** + * @brief Initialize the provided mutex + * + * Call this function to initialize the mutex + * + * @param IoT_Mutex_t - pointer to the mutex to be initialized + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_init(IoT_Mutex_t *); + +/** + * @brief Lock the provided mutex + * + * Call this function to lock the mutex before performing a state change + * This is a blocking call. + * + * @param IoT_Mutex_t - pointer to the mutex to be locked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_lock(IoT_Mutex_t *); + +/** + * @brief Lock the provided mutex + * + * Call this function to lock the mutex before performing a state change. + * This is not a blocking call. + * + * @param IoT_Mutex_t - pointer to the mutex to be locked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_trylock(IoT_Mutex_t *); + +/** + * @brief Unlock the provided mutex + * + * Call this function to unlock the mutex before performing a state change + * + * @param IoT_Mutex_t - pointer to the mutex to be unlocked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_unlock(IoT_Mutex_t *); + +/** + * @brief Destroy the provided mutex + * + * Call this function to destroy the mutex + * + * @param IoT_Mutex_t - pointer to the mutex to be destroyed + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_destroy(IoT_Mutex_t *); + +#ifdef __cplusplus +} +#endif + +#endif /*__THREADS_INTERFACE_H_*/ +#endif /*_ENABLE_THREAD_SUPPORT_*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/timer_interface.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Allan Stockdill-Mander - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file timer_interface.h + * @brief awsTimer interface definition for MQTT client. + * + * Defines an interface to timers that can be used by other system + * components. MQTT client requires timers to handle timeouts and + * MQTT keep alive. + * Starting point for porting the SDK to the timer hardware layer of a new platform. + */ + +#ifndef __TIMER_INTERFACE_H_ +#define __TIMER_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The platform specific timer header that defines the awsTimer struct + */ +#include "timer_platform.h" + +#include <stdint.h> +#include <stdbool.h> + +/** + * @brief awsTimer Type + * + * Forward declaration of a timer struct. The definition of this struct is + * platform dependent. When porting to a new platform add this definition + * in "timer_<platform>.h" and include that file above. + * + */ +typedef struct awsTimer awsTimer; + +/** + * @brief Check if a timer is expired + * + * Call this function passing in a timer to check if that timer has expired. + * + * @param awsTimer - pointer to the timer to be checked for expiration + * @return bool - true = timer expired, false = timer not expired + */ +bool has_timer_expired(awsTimer *); + +/** + * @brief Create a timer (milliseconds) + * + * Sets the timer to expire in a specified number of milliseconds. + * + * @param awsTimer - pointer to the timer to be set to expire in milliseconds + * @param uint32_t - set the timer to expire in this number of milliseconds + */ +void countdown_ms(awsTimer *, uint32_t); + +/** + * @brief Create a timer (seconds) + * + * Sets the timer to expire in a specified number of seconds. + * + * @param awsTimer - pointer to the timer to be set to expire in seconds + * @param uint32_t - set the timer to expire in this number of seconds + */ +void countdown_sec(awsTimer *, uint32_t); + +/** + * @brief Check the time remaining on a given timer + * + * Checks the input timer and returns the number of milliseconds remaining on the timer. + * + * @param awsTimer - pointer to the timer to be set to checked + * @return int - milliseconds left on the countdown timer + */ +uint32_t left_ms(awsTimer *); + +/** + * @brief Initialize a timer + * + * Performs any initialization required to the timer passed in. + * + * @param awsTimer - pointer to the timer to be initialized + */ +void init_timer(awsTimer *); + +#ifdef __cplusplus +} +#endif + +#endif //__TIMER_INTERFACE_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/mbed-os/#c05d72c3c005fbb7e92c3994c32bda45218ae7fe
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_app.json Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,29 @@ +{ + "macros": [ + "MBEDTLS_USER_CONFIG_FILE=\"mbedtls_entropy_config.h\"", + "ENABLE_IOT_INFO=1", + "ENABLE_IOT_WARN=1", + "ENABLE_IOT_ERROR=1", + "DEFAULT_APN=\"m2m.com.attz\"" + ], + "config": { + "network-interface":{ + "help": "options are ETHERNET, CELLULAR_WNC14A2A, CELLULAR_BG96", + "value": "CELLULAR_BG96" + }, + "bg96-debug": { + "help" : "enable or disable BG96 debug messages.", + "value": "false" + }, + "bg96-debug_setting": { + "help" : "bit value 1 and/or 2 enable debug output, bit value 4 enables mbed driver debug output.", + "value": "0xac" + } + }, + "target_overrides": { + "*": { + "platform.stdio-baud-rate": 115200, + "platform.stdio-convert-newlines": true + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_settings.py Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,46 @@ +""" +mbed SDK +Copyright (c) 2016 ARM Limited + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from os.path import join, abspath, dirname + +#ROOT = abspath(join(dirname(__file__), ".")) + +############################################################################## +# Build System Settings +############################################################################## +#BUILD_DIR = abspath(join(ROOT, "build")) + +# ARM +#ARM_PATH = "C:/Program Files/ARM" + +# GCC ARM +#GCC_ARM_PATH = "" +GCC_ARM_PATH = "/usr/local/gcc-arm-none-eabi-7-2017-q4-major/bin/" + +# GCC CodeRed +#GCC_CR_PATH = "C:/code_red/RedSuite_4.2.0_349/redsuite/Tools/bin" + +# IAR +#IAR_PATH = "C:/Program Files (x86)/IAR Systems/Embedded Workbench 7.0/arm" + +# Goanna static analyser. Please overload it in private_settings.py +#GOANNA_PATH = "c:/Program Files (x86)/RedLizards/Goanna Central 3.2.3/bin" + +#BUILD_OPTIONS = [] + +# mbed.org username +#MBED_ORG_USER = ""
Binary file mbed_settings.pyc has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbedtls_entropy_config.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#if !defined(MBEDTLS_ENTROPY_HARDWARE_ALT) && \ + !defined(MBEDTLS_ENTROPY_NV_SEED) && !defined(MBEDTLS_TEST_NULL_ENTROPY) +#error "This hardware does not have an entropy source." +#endif /* !MBEDTLS_ENTROPY_HARDWARE_ALT && !MBEDTLS_ENTROPY_NV_SEED && + * !MBEDTLS_TEST_NULL_ENTROPY */ + +#define MBEDTLS_SHA1_C + +/* + * This value is sufficient for handling 2048 bit RSA keys. + * + * Set this value higher to enable handling larger keys, but be aware that this + * will increase the stack usage. + */ +#define MBEDTLS_MPI_MAX_SIZE 256 + +#define MBEDTLS_MPI_WINDOW_SIZE 1 +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/common/timer.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,119 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file timer.c + * @brief Linux implementation of the timer interface. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stddef.h> +//#include <sys/types.h> +#include <stdint.h> +#include <stdbool.h> + +#include <stdio.h> +//#include <sys/time.h> +#include <time.h> + + +#include "timer_platform.h" + +#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) +#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#define timercmp(tvp, uvp, cmp) \ + (((tvp)->tv_sec == (uvp)->tv_sec) ? \ + ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ + ((tvp)->tv_sec cmp (uvp)->tv_sec)) +#define timeradd(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) +#define timersub(tvp, uvp, vvp) \ + do { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < (time_t)0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) + +volatile uint32_t mscount = 0; + +void onMillisecondTicker(void){ + mscount++; +} + +void mbed_gettimeofday(struct timeval *now) +{ + uint32_t cnt = mscount; + now->tv_sec = time(NULL); + now->tv_usec= cnt - ((long)cnt/1000)*1000; +} + + +bool has_timer_expired(awsTimer *timer) { + struct timeval now, res; + mbed_gettimeofday(&now); + timersub(&timer->end_time, &now, &res); + return res.tv_sec < 0 || (res.tv_sec == 0 && res.tv_usec <= 0); +} + +void countdown_ms(awsTimer *timer, uint32_t timeout) { + struct timeval now; +#ifdef __cplusplus + struct timeval interval = {timeout / 1000, static_cast<int>((timeout % 1000) * 1000)}; +#else + struct timeval interval = {timeout / 1000, (int)((timeout % 1000) * 1000)}; +#endif + mbed_gettimeofday(&now); + timeradd(&now, &interval, &timer->end_time); +} + +uint32_t left_ms(awsTimer *timer) { + struct timeval now, res; + uint32_t result_ms = 0; + mbed_gettimeofday(&now); + timersub(&timer->end_time, &now, &res); + if(res.tv_sec >= 0) { + result_ms = (uint32_t) (res.tv_sec * 1000 + res.tv_usec / 1000); + } + return result_ms; +} + +void countdown_sec(awsTimer *timer, uint32_t timeout) { + struct timeval now; + struct timeval interval = {timeout, 0}; + mbed_gettimeofday(&now); + timeradd(&now, &interval, &timer->end_time); +} + +void init_timer(awsTimer *timer) { + timer->end_time = (struct timeval) {0, 0}; +} + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/common/timer_platform.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef SRC_PROTOCOL_MQTT_AWS_IOT_EMBEDDED_CLIENT_WRAPPER_PLATFORM_LINUX_COMMON_TIMER_PLATFORM_H_ +#define SRC_PROTOCOL_MQTT_AWS_IOT_EMBEDDED_CLIENT_WRAPPER_PLATFORM_LINUX_COMMON_TIMER_PLATFORM_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file timer_platform.h + */ + #include <time.h> + +//#include <sys/time.h> +//#include <sys/select.h> +#include "timer_interface.h" + +/** + * definition of the Timer struct. Platform specific + */ +struct timeval { + long int tv_sec; + long int tv_usec; + }; + +struct awsTimer { + struct timeval end_time; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_PROTOCOL_MQTT_AWS_IOT_EMBEDDED_CLIENT_WRAPPER_PLATFORM_LINUX_COMMON_TIMER_PLATFORM_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect.lib Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/Avnet/ezconnect/#6f501dd5260bc9bf139446b3a5861abd4bd1a056
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +ref: refs/heads/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/ORIG_HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +6f501dd5260bc9bf139446b3a5861abd4bd1a056
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/config Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,11 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true +[remote "origin"] + fetch = +refs/heads/*:refs/remotes/origin/* + url = https://github.com/Avnet/ezconnect/ +[branch "master"] + remote = origin + merge = refs/heads/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/description Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +Unnamed repository; edit this file 'description' to name the repository.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/hooks/applypatch-msg.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +test -x "$GIT_DIR/hooks/commit-msg" && + exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} +:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/hooks/commit-msg.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/hooks/post-update.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/hooks/pre-applypatch.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/hooks/pre-commit.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,50 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against --
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/hooks/pre-rebase.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/hooks/prepare-commit-msg.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/hooks/update.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 <ref> <oldrev> <newrev>)" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 <ref> <oldrev> <newrev>" >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0
Binary file platform/ezconnect/.git/index has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/info/exclude Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,27 @@ +.hg +.git +.svn +.CVS +.cvs +*.orig +.build +.export +.msub +.meta +.ctags* +*.uvproj +*.uvopt +*.project +*.cproject +*.launch +*.ewp +*.eww +Makefile +Debug +*.htm +*.settings +mbed_settings.py +*.py[cod] +# subrepo ignores +BG96-driver +wnc14a2a-driver \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/logs/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 6f501dd5260bc9bf139446b3a5861abd4bd1a056 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694017 +0000 clone: from https://github.com/Avnet/ezconnect/ +6f501dd5260bc9bf139446b3a5861abd4bd1a056 6f501dd5260bc9bf139446b3a5861abd4bd1a056 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694018 +0000 checkout: moving from master to 6f501dd5260bc9bf139446b3a5861abd4bd1a056 +6f501dd5260bc9bf139446b3a5861abd4bd1a056 6f501dd5260bc9bf139446b3a5861abd4bd1a056 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694018 +0000 checkout: moving from 6f501dd5260bc9bf139446b3a5861abd4bd1a056 to master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/logs/refs/heads/master Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +0000000000000000000000000000000000000000 6f501dd5260bc9bf139446b3a5861abd4bd1a056 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694017 +0000 clone: from https://github.com/Avnet/ezconnect/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/logs/refs/remotes/origin/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +0000000000000000000000000000000000000000 6f501dd5260bc9bf139446b3a5861abd4bd1a056 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694017 +0000 clone: from https://github.com/Avnet/ezconnect/
Binary file platform/ezconnect/.git/objects/01/df845d2bf937026517cf3a1583775a15f1cb55 has changed
Binary file platform/ezconnect/.git/objects/03/94039c59f383c6851f0acffa63c5181ffdf5fc has changed
Binary file platform/ezconnect/.git/objects/03/940d98a454c50fe92ea3dbebf4d0076b04e1d0 has changed
Binary file platform/ezconnect/.git/objects/05/f5ea538e5742944b9871223e3528d30ac53718 has changed
Binary file platform/ezconnect/.git/objects/0b/2eeaaeebf9e89bf6072b1032d886482bff3b35 has changed
Binary file platform/ezconnect/.git/objects/0b/30aad9dff7957e3f3bb1663ea27e42bc865bdc has changed
Binary file platform/ezconnect/.git/objects/0e/27abe4614fbc6ac5f8d785343763ed82b2ee99 has changed
Binary file platform/ezconnect/.git/objects/12/c4e5de92ae5db5efd8f791c15b6fa0a527408b has changed
Binary file platform/ezconnect/.git/objects/16/17144b44c044e262745189553cb92009afa9d5 has changed
Binary file platform/ezconnect/.git/objects/16/561a3d208cbb934c81fde9de98193757e29aa6 has changed
Binary file platform/ezconnect/.git/objects/25/1a1180f3a06a40eb7b4d6fc6bc2e2c1c3788d8 has changed
Binary file platform/ezconnect/.git/objects/28/21b38912ffc2700d48364f2495d492cf8bdf46 has changed
Binary file platform/ezconnect/.git/objects/2b/033218c8a73a70d7b3daaf2b78eaa321bbc7bd has changed
Binary file platform/ezconnect/.git/objects/2b/17c7790d1718a481f175ef4ce9ab92ef53829d has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/objects/2c/c72bc3e87b232594266b186b14c3a9a3dda46a Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,2 @@ +xÁI@@Pk§XSô²´q.¿ 1 &q{ï¹õpI££)ónÆ9LËåØhñë·ï¥bêz®²á_\zËE*x©K£ ¨ [Ë +'ÿHÝQ \ No newline at end of file
Binary file platform/ezconnect/.git/objects/30/683a927937021b308035ba4e43da9517ac35ca has changed
Binary file platform/ezconnect/.git/objects/31/068ad77a1445e89d585afa8e34777d273187c2 has changed
Binary file platform/ezconnect/.git/objects/32/7f3128b91c04a80085053038d9ed6d88f7dccb has changed
Binary file platform/ezconnect/.git/objects/34/867dd7a0c0546f66d4ad932b49a457a7edb224 has changed
Binary file platform/ezconnect/.git/objects/3b/568151762adb75907c15472aff57c72c546c80 has changed
Binary file platform/ezconnect/.git/objects/3b/e52f3cfe06f30b1e2a8f56a8efd55c9132afad has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/objects/41/57147708df60759c7b9707716bdb3efad38c15 Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,3 @@ +xM +Â0F]ç¹2dò"^eL´bÚRÒ ·7+àîãñ|ymmîM<õ]DÛ|4dG.S !r¹V +9`&çsµñ.K¾V«¯&#ȱç(µådìH¹(>úsÝõ«¾?Ëb0éëoÞç÷%¯í¦ ¡EÉ} »üªc+Ü¥¨/CëC] \ No newline at end of file
Binary file platform/ezconnect/.git/objects/4d/940ef347e5505390b5d2c8e756893a76c6c02f has changed
Binary file platform/ezconnect/.git/objects/4e/87285d6dc776833ad3261ef7cdd44d3e1b43d4 has changed
Binary file platform/ezconnect/.git/objects/4f/8ebea61ac9c4953c93578f7447d0791e855676 has changed
Binary file platform/ezconnect/.git/objects/51/2c3e8823e98b96f1d6c09efe0a6fcfb2b75916 has changed
Binary file platform/ezconnect/.git/objects/53/fb30cedd48930637b6ad5fbd9fd35e5a117121 has changed
Binary file platform/ezconnect/.git/objects/54/e242fcbb0497ed1cabd41c4e79bba600e4f548 has changed
Binary file platform/ezconnect/.git/objects/5a/7a02f68d7b792257a0683634456589c41e280e has changed
Binary file platform/ezconnect/.git/objects/5b/b7746af7b0f0227140d93461b46938522bfbc5 has changed
Binary file platform/ezconnect/.git/objects/61/3232f41c73ab7077bda43cf481444cff08801d has changed
Binary file platform/ezconnect/.git/objects/62/929c33217ab73f842678db2452778989e0c4e2 has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/objects/63/c43abf9b14fb1151a7b4ecb0ae1b19e6148c4a Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,2 @@ +xÁË@0Pk_!±fТciã;z§ñ +%ñ÷ÎÁz 2*¼?ïhýô c£eX¿}Ϧ¶ã*q×üöŹk ¶R+Ê¢R"¥/µqÚ*þHZ* \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/objects/68/a62a53751956bd5a916ba06ed6535eef9d1fac Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,3 @@ +x+)JMU025a040031Qpr·4ÓM)Ê,K-ÒËÉLbðk×Í=^ÖluYMîûÙ+¾vÒÎW J\]|]õrSX¿¾ +îwâ=£PÉÎTã2×Qs ¨¢ÔÄâJÝäü¼¼Ôä½äÛë]¦q +L¾Îpöû³ÅÌÏwEbSÁ0éÜÉ;ïå.;þ6ÏI[¥½Ðª27)5%è<½¬âü<±0)[ Ý}ÿ¾¼7CÒ<üѬeP åyÉ&FÈ^z}O©çîè>áÆîâòaë \ No newline at end of file
Binary file platform/ezconnect/.git/objects/6e/807dff8d3e921c5c26cbcc62a6d328eaeda29e has changed
Binary file platform/ezconnect/.git/objects/6f/501dd5260bc9bf139446b3a5861abd4bd1a056 has changed
Binary file platform/ezconnect/.git/objects/70/3bb22d04053f314ebd3673e0218cc271b16574 has changed
Binary file platform/ezconnect/.git/objects/74/b9b838c5324a8364de7d4bfd486acc630f2353 has changed
Binary file platform/ezconnect/.git/objects/76/7735ed8724bff8c90aa6899ffba2dd8b3771b9 has changed
Binary file platform/ezconnect/.git/objects/83/399c54747c2d36ae20cf4955ea9af6e7807bce has changed
Binary file platform/ezconnect/.git/objects/90/e1843a1728494fc4fc9f1bcd74d6d014af6757 has changed
Binary file platform/ezconnect/.git/objects/92/cec9dcef531ed0cec7ed610e422b248007713c has changed
Binary file platform/ezconnect/.git/objects/95/f6f97aa301add43e04a1bc58b33e716d215ff8 has changed
Binary file platform/ezconnect/.git/objects/98/e00cf70d6abd3c8860983f9820e2b38a61daf9 has changed
Binary file platform/ezconnect/.git/objects/98/e83b128e3c857a877ed6c43d692159c0f8a3d2 has changed
Binary file platform/ezconnect/.git/objects/99/ac77bc801cfc9d5dcae341711b63457f0f1bc4 has changed
Binary file platform/ezconnect/.git/objects/a4/c219072b73f7cb7eb70481706283cae715b41c has changed
Binary file platform/ezconnect/.git/objects/a5/da9f05a3ca5dd9ff1ef70b37df7cd8c111af5e has changed
Binary file platform/ezconnect/.git/objects/a7/2231a5ff58341194c140ca964c5b1d0af72bb0 has changed
Binary file platform/ezconnect/.git/objects/a9/bd46a38406e1450f8bfb22eee73e2c312f4612 has changed
Binary file platform/ezconnect/.git/objects/ac/865f7804f9512fbd25cd249372dc9b3e6347dc has changed
Binary file platform/ezconnect/.git/objects/ae/8aff9f8586807a558a8e4a2321a8be97beb4b0 has changed
Binary file platform/ezconnect/.git/objects/be/2c61b13ede13ebe80f823288f2dc0425cef970 has changed
Binary file platform/ezconnect/.git/objects/c7/23e71e6a1834aae47cffb5fb3d1ae82bd261ca has changed
Binary file platform/ezconnect/.git/objects/c9/572570995432e78be15f788deab8c1e280a232 has changed
Binary file platform/ezconnect/.git/objects/d5/27a76e40a82b5c0f7be5c4123fc8336bf68b07 has changed
Binary file platform/ezconnect/.git/objects/d5/b48a39f7a1815aeb532326e3b34ba8985ad219 has changed
Binary file platform/ezconnect/.git/objects/d6/d8ea12f24a8c316273bdd54438e03264cd7025 has changed
Binary file platform/ezconnect/.git/objects/d9/30a10052739cd8b35257dc8feed59822aab2fb has changed
Binary file platform/ezconnect/.git/objects/da/9c00514500b899180fb2c51d09dcb6a692f6cb has changed
Binary file platform/ezconnect/.git/objects/db/af4496091093d700cdf7b8cd73490309e7ba59 has changed
Binary file platform/ezconnect/.git/objects/dd/ddab380f18d1d300bb99abf2fb5032c45822b9 has changed
Binary file platform/ezconnect/.git/objects/e2/565b6bdfb00c7549bb505b8e13818b73f81fd3 has changed
Binary file platform/ezconnect/.git/objects/e3/2b4de0906509a22665a50f066089257071ca37 has changed
Binary file platform/ezconnect/.git/objects/e4/2782f0f11523a30bb43f0b5dcd189ac9552c2c has changed
Binary file platform/ezconnect/.git/objects/e8/53ceff5d9488b5b1aee4f3e21ee071cdd7e8e7 has changed
Binary file platform/ezconnect/.git/objects/ea/57df685ed00ab2ace73eb301f24cd0d27bab8d has changed
Binary file platform/ezconnect/.git/objects/ef/332d2a077ae291ffb4c5f4e2c8161611cdfa35 has changed
Binary file platform/ezconnect/.git/objects/fb/2f1d5d9e6e501faf6c69bdac0daa7b29c41d75 has changed
Binary file platform/ezconnect/.git/objects/fb/376f415e17fce7ce51f79fdc45c53177d35ebf has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/packed-refs Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,3 @@ +# pack-refs with: peeled +2b033218c8a73a70d7b3daaf2b78eaa321bbc7bd refs/remotes/origin/jflynn129-patch-1 +6f501dd5260bc9bf139446b3a5861abd4bd1a056 refs/remotes/origin/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/refs/heads/master Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +6f501dd5260bc9bf139446b3a5861abd4bd1a056
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.git/refs/remotes/origin/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +ref: refs/remotes/origin/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/.msub Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,2 @@ +BG96-driver = https://github.com/jflynn129/BG96-driver/#13:620df10719ecc4787da5a466b98dce0987f84f1e +wnc14a2a-driver = https://github.com/Avnet/wnc14a2a-driver/#18:90928b81747ef4b0fb4fdd94705142175e014b30
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver.lib Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/jflynn129/BG96-driver/#620df10719ecc4787da5a466b98dce0987f84f1e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +ref: refs/heads/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/ORIG_HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +620df10719ecc4787da5a466b98dce0987f84f1e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/config Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,11 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true +[remote "origin"] + fetch = +refs/heads/*:refs/remotes/origin/* + url = https://github.com/jflynn129/BG96-driver/ +[branch "master"] + remote = origin + merge = refs/heads/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/description Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +Unnamed repository; edit this file 'description' to name the repository.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/hooks/applypatch-msg.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +test -x "$GIT_DIR/hooks/commit-msg" && + exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} +:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/hooks/commit-msg.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/hooks/post-update.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/hooks/pre-applypatch.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/hooks/pre-commit.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,50 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against --
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/hooks/pre-rebase.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/hooks/prepare-commit-msg.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/hooks/update.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 <ref> <oldrev> <newrev>)" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 <ref> <oldrev> <newrev>" >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0
Binary file platform/ezconnect/BG96-driver/.git/index has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/info/exclude Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,25 @@ +.hg +.git +.svn +.CVS +.cvs +*.orig +.build +.export +.msub +.meta +.ctags* +*.uvproj +*.uvopt +*.project +*.cproject +*.launch +*.ewp +*.eww +Makefile +Debug +*.htm +*.settings +mbed_settings.py +*.py[cod] +# subrepo ignores \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/logs/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 620df10719ecc4787da5a466b98dce0987f84f1e www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694021 +0000 clone: from https://github.com/jflynn129/BG96-driver/ +620df10719ecc4787da5a466b98dce0987f84f1e 620df10719ecc4787da5a466b98dce0987f84f1e www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694022 +0000 checkout: moving from master to 620df10719ecc4787da5a466b98dce0987f84f1e +620df10719ecc4787da5a466b98dce0987f84f1e 620df10719ecc4787da5a466b98dce0987f84f1e www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694022 +0000 checkout: moving from 620df10719ecc4787da5a466b98dce0987f84f1e to master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/logs/refs/heads/master Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +0000000000000000000000000000000000000000 620df10719ecc4787da5a466b98dce0987f84f1e www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694021 +0000 clone: from https://github.com/jflynn129/BG96-driver/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/logs/refs/remotes/origin/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +0000000000000000000000000000000000000000 620df10719ecc4787da5a466b98dce0987f84f1e www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694021 +0000 clone: from https://github.com/jflynn129/BG96-driver/
Binary file platform/ezconnect/BG96-driver/.git/objects/01/649b83f8f2bade4af50b7e363a72e7c8514575 has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/objects/05/8c7b574d6da55cefa6e45636d3dd20f9baa6e7 Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,4 @@ +xAJ1E]çuJ§®7 +kIR1C'z¢èíí ¸v÷yü/õZËÉáÕØD ¸ÀÉdIFòBIô¢á9qD±Ä:MÚÎ,8PN^DÈÙý«LDYÈ£´õÂ*|÷¾Á)¯ßéÉÃÍß¼;ÖPÖCêõ´3~v×8#ªîCþ¯zû«ª\¾àþåáõéñÎ¥K.M.Ã&µ +CÂokÓ¥7õ? +S³ \ No newline at end of file
Binary file platform/ezconnect/BG96-driver/.git/objects/08/1d21bafc553ff6f6d3220b33b26997ca45915b has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/09/c3e4dfb706c5002f75f03853fbde6cabb6e3ac has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/0b/2f7599a580fb46ed1bedeac974abfe3775166f has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/0d/3689fbd5c8c179b8fa29a62265bd1b027a9743 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/10/c6b9b3ba03a64e46fb28d65efa8033a70fa77a has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/12/c7951015200d16b41bd6155bc2dcfc02ea143b has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/1d/039080b0f1a5e700bf04778b7d7e8526b69831 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/1f/00b11a8caeb24b4ad7ebd585168cdc3ac7b0f1 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/20/390f9da82ca30c89ebd8aa8d777242cebee7db has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/20/cf0a6c77d952824f98f9b9374eaf7d8d5325ff has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/22/608453fc47aa779ebe0c375651ca736cf2beb4 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/2a/c1a8e34e716839044bfeae19b247b5e6dbf449 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/2c/00117d3cd149252fb6cbe137f1d88b85257d53 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/34/2cbe80538118d7b8dd804702756dc7dca2b62c has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/37/d26cc01f48bf2638ea0f0f278b2a3c3e33691b has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/3d/d08ef5e5ceaa588c01ef9304f0715dbdcd6bbf has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/40/4e46f6f084685badecd730dfdf8dd30673c17a has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/44/e00024054b7aa4edd8ed10a4f94f74881c0999 has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/objects/45/ea798b1bf39f65c883da1cc2ec85772aac206f Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +x[N!Dýf½ ÍcäƸºé¹¢cÄëî/à_¥r*§ølN0Î?Í! bD.7½y §Ýõä-Q°÷d½úÊCú&ÓR*>XDL¹NÁcq-Å*¯ù~øØÞÑ$¸ýÅ·{Ëõxá³½zcÖÉ!<k§µºÚëàLÕ^0d®Ñá»^ú5aõãäÏÚïÐÖú+Mð \ No newline at end of file
Binary file platform/ezconnect/BG96-driver/.git/objects/4a/ec1ed32321bdaca5b19a78c557fee26aec2b78 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/4c/491edc1c2b4d64bcd8a2112c36c413591f1e5e has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/53/ff7b3a23dc088a4b44e1a6521c070ce1608447 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/57/e6ce57583b8dea98de71f69f87f661e3a1f6bd has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/60/86b05475162ca8c997bebd54cb069f16888e77 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/62/0df10719ecc4787da5a466b98dce0987f84f1e has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/6f/8bd972fc308d08ee136768c3c808c0edf65c95 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/72/0e498939b9d3bcd49b0638b018b6ad77c04b90 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/76/05676433c04e33173e4dee226aadeab4e40f49 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/7b/add56cf7bef7dd0b429179b568c10ffc53da48 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/7d/6b70e5f41a8259bdeca894752804c86dcd19ed has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/82/cecbff8ca661de47c0a8eaac86b6002193ac95 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/95/d97f73d9e9c795ffb98cab3add7e3097684bb5 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/99/ecd33765600691037ec8c80500d99d8b6d2887 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/9b/2b4863880885e2f9fdda14aefce4aa50a704f3 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/a1/2ab6999d57d8d1bcbee2d935c9751d4ca3b871 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/a4/048a1aadb55612f877a5628b027d4d8fa5c254 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/a7/6c34fa3ab30d7a4c2cb0a4cea27dfc9c3d9056 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/a7/eddda2d5309651dd4c7fdc258adad24aab77f7 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/af/b16f0397f114c0e3f54c0a4211f129c52417b3 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/b0/63718da8fc9eee865aab17f888fe89032159ed has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/b0/8440897f54c2dbc4f66f726a96df9ff067d2c4 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/b1/ce8f21428701638be2a384917b41440532ff35 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/b3/ae20c8941e448a131e0f8cf9089e51bd5e7c1d has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/b7/71d3d568f913a3cb947e4757b57df9cd3993cf has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/b7/e68aa9deb4461071c45ca41c88b4e79afee9c8 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/b8/2a833e1b31fb08c71b8f4b2473eee846605efa has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/ba/b007aadc3fec3ef78ce17aab3d4cdb0e58db83 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/be/07108791b09b6c3c5c9f34eb67f08f788758da has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/objects/c4/2c3ac32d803153a83595ee979ec0ecb93dbfa6 Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,2 @@ +xUÛNÛ@í³¿b*ÈDI¨B¥(¸(µR©,_ÖΪÎnj¯iQÅ¿wÆ»"z±xagvÎ9g'~"}8ì¿{e½á"HòAká³°?oÕN=ô)Ej¡ÓñÑáD(F^ÀØÚ øñÑá±e9Lýé·Uf¨¦kjÚ¾ $2v,˼e»ðËü÷®äòàÌ./|<SÔÍò ÀrïícTG`o¡Ø2ÅËÝú2ÂÎÎ4Ù¯0˸8ÝÖ®y>{2½²*ù§¥¯NGçîÙµsáL§.tÏG§³1 ÒnîâWó¢ Ç sµÌdL0øÙ|FÈîsîÇÑííÄ$J?çTË~Á"ÀDÈ#=ÅMìh¡i4Íùx¥ÖØÑÂndaP*O¶ÉEñÍÃ2Ú±fcÆQ$8T¾¢{urf7¸u×}Y9à±`ñ,Éq+zQϵj±aOÃKQ'{áñé·Ã×d§Ô\¬9Pù©<µ¤C¨P/jî¥ÐáK×ÃðA5¦Ôû3å|t¶¡i÷ÐæôPÿ]àÑÍÍõ ôÀ0õë¢n%ö« Â6ÁuÁ03\5¦dcBÇn½Üù)gQå8ä±rà?s,Y +Q.ed-âôæZòµ \¼oJAe±É¦à{Ý ¯\¶½rÙâYë1.Ôg: H=Æðø¼à{ÎStÜÎÚc¡´ØúXÿö¬{ÉÃí4~S뮺mü4Ô¹ðíÍgYf<ñfyL;ø¿³6»íîÔKEûgÛþ(TjìLiÌÖÊè½ß,îlx \ No newline at end of file
Binary file platform/ezconnect/BG96-driver/.git/objects/c4/a4b7ff533879be6bf8573807127a8fe69feed5 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/c5/302e7498f205f646f1880e5a76c61babf98d67 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/c8/915c3ac846e5f2d0854dffd4aa648e4e522411 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/c8/eb881cd8c6065082c9f4b35b5c13bb7371f935 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/c9/525306726ad299aa9e9dc8d971e9dd938a67f2 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/c9/fbebcbad6f21f82a28b1ceef68bee40ccf7ec9 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/cf/4ab19d757920b27dcff343657a733afe30f62c has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/d0/69ccc453a439a049bc539c56d01c22c74104cf has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/d2/b891a618ce9e507bb03a2c8ee485727877846c has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/d5/785e4fb3d374c7494095f2fccf93e05d0e3b8f has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/da/baba208932f29c93e0791aa46c3be196e53077 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/db/a582a60ce2d1f7dcd3bbc32599cc4ffb6eb2fa has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/df/876f86bde264198ede19f77d2c3be2ac37aa66 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/df/8b2ce662ba7c6ef8a34ff1ebd683aa65357142 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/e2/dcc3fe0e22ac8347e8635eb5576df6c737dc68 has changed
Binary file platform/ezconnect/BG96-driver/.git/objects/f5/929cc6efc7143ee4e3efe9ec28949559ed0bcc has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/objects/f9/1ed4773d93e82ebfef146268935b51b6652da8 Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +x+)JMU07`040031Qpr·4ÓK.(`XþöÓïúÔßQíºuÉkuùwd5µÙO¿H5Eî}³bJ©Ëܳor3 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/objects/fd/585c10562193a2b35b8f0d1787c8fd6ef7293a Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,2 @@ +x_OÂ0Å}æSÜôDðA0j}"-»Ñ.mǾ»ÝÝ1û²¦=íùݳ[kAçnxõÓ?â[dÀD4ì_ÇRn¬ï-µZÉÈïæZÒ.DV×ioqÂÀ_A¥¥éèyØ:[´Gho +ìh6ØÇ)±8ã¿rîÎIÕD!¤ºà*¼õ8](Ðrº$um(½B`a+0ÐÈú*²z!W×cw^¶;T¤z(1ØÇ$RÁÊ#Z4Çà³W¸tR+p(¾lºFxý||¼ÁHsÖF84Ù¿tHÐÿÆúX¨ìY¦Éâ©/ö¾ÁbÏ7YS-\ æ^Òl ÉÞlðxÁã}>N¾/¸tÎ\¨ÿÞgÝBûf42D[5cÓ~喙T¾j;6þ¤W¼xj6>3SîÜ:µ~áÕÿl \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/packed-refs Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,3 @@ +# pack-refs with: peeled +058c7b574d6da55cefa6e45636d3dd20f9baa6e7 refs/remotes/origin/jflynn129-dev +620df10719ecc4787da5a466b98dce0987f84f1e refs/remotes/origin/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/refs/heads/master Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +620df10719ecc4787da5a466b98dce0987f84f1e
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/.git/refs/remotes/origin/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +ref: refs/remotes/origin/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/BG96/BG96.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,554 @@ +/** +* copyright (c) 2018-2019, James Flynn +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** +* @file BG96.cpp +* @brief Implements a standard NetworkInterface class for use with Quicktel BG96 +* +* @author James Flynn +* +* @date 19-Mar-2018 +* +*/ + +#include <ctype.h> + +#include "mbed.h" +#include "mbed_debug.h" +#include "BG96.h" + +#define BG96_1s_WAIT 1000 //will wait for 1 second for startup +#define BG96_60s_TO 60000 //wait 60 seconds +#define BG96_150s_TO 150000 //wait 150s (2.5 mins) +#define BG96_TX_TIMEOUT 2000 //time before a TX timeout occurs +#define BG96_RX_TIMEOUT 1000 //time before a TX timeout occurs +#define BG96_WAIT4READY 15000 //wait 15 seconds for 'RDY' after reset +#define BG96_AT_TIMEOUT 1000 //standard AT command timeout +#define BG96_WRK_CONTEXT 1 //we will only use context 1 in driver +#define BG96_CLOSE_TO 1 //wait x seconds for a socket close + +// +// if DEBUG is enabled, these macros are used to dump data arrays +// +#if MBED_CONF_APP_BG96_DEBUG == true +#define TOSTR(x) #x +#define INTSTR(x) TOSTR(x) +#define DUMP_LOC (char*)(__FILE__ ":" INTSTR(__LINE__)) + +#define DUMP_ARRAY(x,s) { \ + int i, k; \ + for (i=0; i<s; i+=16) { \ + printf("[%s]:0x%04X: ",DUMP_LOC,i); \ + for (k=0; k<16; k++) { \ + if( (i+k)<s ) \ + printf("%02X ", x[i+k]); \ + else \ + printf(" "); \ + } \ + printf(" "); \ + for (k=0; k<16; k++) { \ + if( (i+k)<s ) \ + printf("%c", isprint(x[i+k])? x[i+k]:'.'); \ + } \ + printf("\n\r"); \ + } \ + } +#else +#define DUMP_ARRAY(x,s) /* not used */ +#endif + +/** ---------------------------------------------------------- +* @brief constructor +* @param none +* @retval none +*/ +BG96::BG96(bool debug) : + _contextID(1), + _serial(MBED_CONF_BG96_LIBRARY_BG96_TX, MBED_CONF_BG96_LIBRARY_BG96_RX), + _parser(&_serial), + _bg96_reset(MBED_CONF_BG96_LIBRARY_BG96_RESET), + _vbat_3v8_en(MBED_CONF_BG96_LIBRARY_BG96_WAKE), + _bg96_pwrkey(MBED_CONF_BG96_LIBRARY_BG96_PWRKEY) +{ + _serial.set_baud(115200); + _parser.debug_on(debug); + _parser.set_timeout(BG96_AT_TIMEOUT); + _parser.set_delimiter("\r\n"); +} + +BG96::~BG96(void) +{ } + +/** ---------------------------------------------------------- +* @brief get BG96 SW version +* @param none +* @retval string containing SW version +*/ +const char* BG96::getRev(char* combined) +{ + bool ok=false; + char buf1[20], buf2[20]; + + _bg96_mutex.lock(); + ok = (tx2bg96((char*)"AT+CGMM") && _parser.recv("%s\n",buf1) && _parser.recv("OK") && + _parser.send("AT+CGMR") && _parser.recv("%s\n",buf2) && _parser.recv("OK") ); + _bg96_mutex.unlock(); + + if( ok ) + sprintf(combined,"%s Rev:%s",buf1,buf2); + return ok? (const char*) combined : NULL; +} + +/** ---------------------------------------------------------- +* @brief enable AT command tracing +* @param integer, if msb is set, tracing enabled +* @retval none +*/ +void BG96::doDebug(int f) +{ + _parser.debug_on(f&0x80); +} + +/** ---------------------------------------------------------- +* @brief Tx a string to the BG96 and wait for an OK response +* @param none +* @retval true if OK received, false otherwise +*/ +bool BG96::tx2bg96(char* cmd) { + bool ok=false; + _bg96_mutex.lock(); + ok=_parser.send(cmd) && _parser.recv("OK"); + _bg96_mutex.unlock(); + return ok; +} + +/** ---------------------------------------------------------- +* @brief set the contextID for the BG96. This context will +* be used for all subsequent operations +* @param int of desired context. if <1, return the current context +* @retval current context +*/ +/* +* Context can be 1-16 +*/ +int BG96::setContext( int i ) +{ + if( i > 16 ) + return -1; + + if( i < 1 ) + return _contextID; + + return _contextID = i; +} + +/** ---------------------------------------------------------- +* @brief perform a HW reset of the BG96 +* @param none +* @retval none +*/ +void BG96::reset(void) +{ + _bg96_reset = 0; + _bg96_pwrkey = 0; + _vbat_3v8_en = 0; + wait_ms(300); + + _bg96_reset = 1; + _vbat_3v8_en = 1; + _bg96_pwrkey = 1; + wait_ms(400); + + _bg96_reset = 0; + wait_ms(10); +} + +/** ---------------------------------------------------------- +* @brief wait for 'RDY' response from BG96 +* @param none +* @retval true if 'RDY' received, false otherwise +*/ +bool BG96::BG96Ready(void) +{ + Timer t; + int done=false; + + _bg96_mutex.lock(); + reset(); + t.start(); + while( !done && t.read_ms() < BG96_WAIT4READY ) + done = _parser.recv("RDY"); + _bg96_mutex.unlock(); + return done; +} + + +/** ---------------------------------------------------------- +* @brief startup BG96 module +* @param none +* @retval true if successful, false otherwise +*/ +bool BG96::startup(void) +{ + int done=false; + + if( !BG96Ready() ) + return false; + + _bg96_mutex.lock(); + _parser.set_timeout(BG96_1s_WAIT); + if( tx2bg96((char*)"ATE0") ) + done = tx2bg96((char*)"AT+COPS?"); + _parser.set_timeout(BG96_AT_TIMEOUT); + _bg96_mutex.unlock(); + return done; + } + + +/** ---------------------------------------------------------- +* @brief connect to APN +* @param apn string +* @param username (not used) +* @param password (not used) +* @retval nsapi_error_t +*/ +nsapi_error_t BG96::connect(const char *apn, const char *username, const char *password) +{ + char cmd[100],_apn[50]; + bool done = false; + Timer t; + int cntx; + + _bg96_mutex.lock(); + t.start(); + do { + _parser.send("AT+QICSGP=%d",_contextID); + done = _parser.recv("+QICSGP: %d, \"%50[^\"]\"",&cntx, _apn); + wait_ms(2); + } + while( !done && t.read_ms() < BG96_60s_TO ); + + if( !done ) { + _bg96_mutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } + + _parser.flush(); + if( strcmp(_apn,apn) ) { + sprintf(cmd,"AT+QICSGP=%d,1,\"%s\",\"%s\",\"%s\",0", _contextID, &apn[0], &username[0], &password[0]); + if( !tx2bg96(cmd) ) { + _bg96_mutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } + } + + sprintf(cmd,"AT+QIACT=%d", _contextID); + t.reset(); + done=false; + while( !done && t.read_ms() < BG96_150s_TO ) + done = tx2bg96(cmd); + + _bg96_mutex.unlock(); + + return done? NSAPI_ERROR_OK : NSAPI_ERROR_DEVICE_ERROR; +} + +/** ---------------------------------------------------------- +* @brief disconnect from an APN +* @param none +* @retval true/false if disconnect was successful or not +*/ +bool BG96::disconnect(void) +{ + char buff[15]; + _parser.set_timeout(BG96_60s_TO); + sprintf(buff,"AT+QIDEACT=%d\r",_contextID); + bool ok = tx2bg96(buff); + _parser.set_timeout(BG96_AT_TIMEOUT); + return ok; +} + +/** ---------------------------------------------------------- +* @brief perform DNS lookup of URL to determine IP address +* @param string containing the URL +* @retval string containing the IP results from the URL DNS +*/ +bool BG96::resolveUrl(const char *name, char* ipstr) +{ + char buf2[50]; + bool ok; + int err, ipcount, dnsttl; + + _bg96_mutex.lock(); + _parser.set_timeout(BG96_60s_TO); + ok = ( _parser.send("AT+QIDNSGIP=%d,\"%s\"",_contextID,name) + && _parser.recv("OK") + && _parser.recv("+QIURC: \"dnsgip\",%d,%d,%d",&err, &ipcount, &dnsttl) + && err==0 + && ipcount > 0 + ); + + if( ok ) { + _parser.recv("+QIURC: \"dnsgip\",\"%[^\"]\"",ipstr); //use the first DNS value + for( int i=0; i<ipcount-1; i++ ) + _parser.recv("+QIURC: \"dnsgip\",\"%[^\"]\"", buf2); //and discrard the rest if >1 + } + _parser.set_timeout(BG96_AT_TIMEOUT); + _bg96_mutex.unlock(); + + return ok; +} + +/** ---------------------------------------------------------- +* @brief determine if BG96 is readable +* @param none +* @retval true/false +*/ +bool BG96::readable() +{ + return _serial.readable(); +} + +/** ---------------------------------------------------------- +* @brief determine if BG96 is writable +* @param none +* @retval true/false +*/ +bool BG96::writeable() +{ + return _serial.writable(); +} + + +/** ---------------------------------------------------------- +* @brief obtain the IP address socket is using +* @param none +* @retval string containing IP or NULL on failure +*/ +const char *BG96::getIPAddress(char *ipstr) +{ + int cs, ct; + bool done=false; + + _bg96_mutex.lock(); + _parser.set_timeout(BG96_150s_TO); + done = _parser.send("AT+QIACT?") && _parser.recv("+QIACT: 1, %d,%d,\"%16[^\"]\"",&cs,&ct,ipstr); + _parser.set_timeout(BG96_AT_TIMEOUT); + _bg96_mutex.unlock(); + + return done? ipstr:NULL; +} + +/** ---------------------------------------------------------- +* @brief return the MAC +* @param none +* @retval string containing the MAC or NULL on failure +* MAC is created using the ICCID of the SIM +*/ +const char *BG96::getMACAddress(char* sn) +{ + + _bg96_mutex.lock(); + if( _parser.send("AT+QCCID") ) { + _parser.recv("+QCCID: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", + &sn[26], &sn[25], &sn[24],&sn[23],&sn[22], + &sn[21], &sn[19], &sn[18],&sn[16],&sn[15], + &sn[13], &sn[12], &sn[10],&sn[9], &sn[7], + &sn[6], &sn[4], &sn[3], &sn[1], &sn[0]); + sn[2] = sn[5] = sn[8] = sn[11] = sn[14] = sn[17] = ':'; + sn[20] = 0x00; + } + _bg96_mutex.unlock(); + + return (const char*)sn; +} + +/** ---------------------------------------------------------- +* @brief determine if BG96 is connected to an APN +* @param none +* @retval true or false +*/ +bool BG96::isConnected(void) +{ + char ip[25]; + return getIPAddress(ip) != NULL; +} + +/** ---------------------------------------------------------- +* @brief open a BG96 socket +* @param type of socket to open ('u' or 't') +* @param id of BG96 socket +* @param address (IP) +* @param port of the socket +* @retval true if successful, else false on failure +*/ +bool BG96::open(const char type, int id, const char* addr, int port) +{ + char* stype = (char*)"TCP"; + char cmd[20]; + int err=1; + bool ok; + + if( type == 'u' ) + stype = (char*)"UDP"; + + _bg96_mutex.lock(); + sprintf(cmd,"+QIOPEN: %d,%%d", id); + _parser.set_timeout(BG96_150s_TO); + ok=_parser.send("AT+QIOPEN=%d,%d,\"%s\",\"%s\",%d,0,0\r", _contextID, id, stype, addr, port) + && _parser.recv(cmd, &err) + && err == 0; + _parser.set_timeout(BG96_AT_TIMEOUT); + _bg96_mutex.unlock(); + if( ok ) + while( recv(id, cmd, sizeof(cmd)) ) + /* clear out any residual data in BG96 buffer */; + + return ok; +} + +/** ---------------------------------------------------------- +* @brief get last error code +* @param none. +* @retval returns true/false if successful and updated error string +*/ +bool BG96::getError(char *str) +{ + char lstr[4]; + int err; + memset(lstr,0x00,sizeof(lstr)); + _bg96_mutex.lock(); + bool done = (_parser.send("AT+QIGETERROR") + && _parser.recv("+QIGETERROR: %d,%[^\\r]",&err,lstr) + && _parser.recv("OK") ); + _bg96_mutex.unlock(); + if( done ) + sprintf(str,"Error:%d",err); + return done; +} + + +/** ---------------------------------------------------------- +* @brief close the BG96 socket +* @param id of BG96 socket +* @retval true of close successful false on failure. <0 if error +*/ +bool BG96::close(int id) +{ + bool done=false; + + _bg96_mutex.lock(); + _parser.set_timeout(BG96_150s_TO); + done = (_parser.send("AT+QICLOSE=%d,%d", id, BG96_CLOSE_TO) && _parser.recv("OK")); + _parser.set_timeout(BG96_AT_TIMEOUT); + _bg96_mutex.unlock(); + return done; +} + +/** ---------------------------------------------------------- +* @brief send data to the BG96 +* @param id of BG96 socket +* @param pointer to the data to send +* @param number of bytes to send +* @retval true if send successfull false otherwise +*/ +bool BG96::send(int id, const void *data, uint32_t amount) +{ + bool done; + + _bg96_mutex.lock(); + _parser.set_timeout(BG96_TX_TIMEOUT); + + done = !_parser.send("AT+QISEND=%d,%ld", id, amount); + if( !done && _parser.recv(">") ) + done = (_parser.write((char*)data, (int)amount) <= 0); + + if( !done ) + done = _parser.recv("SEND OK"); + _parser.set_timeout(BG96_AT_TIMEOUT); + _bg96_mutex.unlock(); + + return done; +} + +/** ---------------------------------------------------------- +* @brief check if RX data has arrived +* @param id of BG96 socket +* @retval true/false +*/ +bool BG96::chkRxAvail(int id) +{ + char cmd[20]; + + sprintf(cmd, "+QIURC: \"recv\",%d", id); + _parser.set_timeout(1); + int i = _parser.recv(cmd); + _parser.set_timeout(BG96_AT_TIMEOUT); + return i; +} + +/** ---------------------------------------------------------- +* @brief check for the amount of data available to read +* @param id of BG96 socket +* @retval number of bytes in RX buffer or 0 +*/ +int BG96::rxAvail(int id) +{ + int trl, hrl, url; + + _bg96_mutex.lock(); + bool done = ( _parser.send("AT+QIRD=%d,0",id) && _parser.recv("+QIRD:%d,%d,%d",&trl, &hrl, &url) ); + _bg96_mutex.unlock(); + if( done ) + return trl-hrl; + return 0; +} + + +/** ---------------------------------------------------------- +* @brief receive data from BG96 +* @param id of BG96 socket +* @param pointer to location to store returned data +* @param count of the number of bytes to get +* @retval number of bytes returned or 0 +*/ +int32_t BG96::recv(int id, void *data, uint32_t cnt) +{ + int rxCount, ret_cnt=0; + + _bg96_mutex.lock(); + chkRxAvail(id); + + if( _parser.send("AT+QIRD=%d,%d",id,(int)cnt) && _parser.recv("+QIRD:%d\r\n",&rxCount) ) { + if( rxCount > 0 ) { + _parser.getc(); //for some reason BG96 always outputs a 0x0A before the data + _parser.read((char*)data, rxCount); + + if( !_parser.recv("OK") ) + rxCount = NSAPI_ERROR_DEVICE_ERROR; + } + ret_cnt = rxCount; + } + else + ret_cnt = NSAPI_ERROR_DEVICE_ERROR; + _bg96_mutex.unlock(); + return ret_cnt; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/BG96/BG96.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,240 @@ +/** +* copyright (c) 2018, James Flynn +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** +* @file BG96.h +* @brief Implements NetworkInterface class for use with the Quectel BG96 +* data module running MBed OS v5.x +* +* @author James Flynn +* +* @date 1-April-2018 +*/ + +#ifndef __BG96_H__ +#define __BG96_H__ + +#include "mbed.h" + +// If target board does not support Arduino pins, define pins as Not Connected +#if defined(TARGET_FF_ARDUINO) +#if !defined(MBED_CONF_BG96_LIBRARY_BG96_TX) +#define MBED_CONF_BG96_LIBRARY_BG96_TX D8 +#endif +#if !defined(MBED_CONF_BG96_LIBRARY_BG96_RX) +#define MBED_CONF_BG96_LIBRARY_BG96_RX D2 +#endif +#if !defined(MBED_CONF_BG96_LIBRARY_BG96_RESET) +#define MBED_CONF_BG96_LIBRARY_BG96_RESET D7 +#endif +#if !defined(MBED_CONF_BG96_LIBRARY_BG96_WAKE) +#define MBED_CONF_BG96_LIBRARY_BG96_WAKE D11 +#endif +#if !defined(MBED_CONF_BG96_LIBRARY_BG96_PWRKEY) +#define MBED_CONF_BG96_LIBRARY_BG96_PWRKEY D10 +#endif +#else // !defined(TARGET_FF_ARDUINO) +#define MBED_CONF_BG96_LIBRARY_BG96_TX NC +#define MBED_CONF_BG96_LIBRARY_BG96_RX NC +#define MBED_CONF_BG96_LIBRARY_BG96_RESET NC +#define MBED_CONF_BG96_LIBRARY_BG96_WAKE NC +#define MBED_CONF_BG96_LIBRARY_BG96_PWRKEY NC +#endif // !defined(TARGET_FF_ARDUINO) + +/** BG96Interface class. + Interface to a BG96 module. + */ + +class BG96 +{ +public: + static const unsigned BG96_BUFF_SIZE = 1500; + + BG96(bool debug=false); + ~BG96(); + + /** + * Init the BG96 + * + * @param mode mode in which to startup + * @return true only if BG96 has started up correctly + */ + bool startup(void); + + /** + * Wait for 'RDY' signal or timeout waiting... + * + * @return none. + */ + void waitBG96Ready(void); + + /** + * Reset BG96 + * + * @return true if BG96 resets successfully + */ + void reset(void); + + /** + * Connect BG96 to APN + * + * @param apn the name of the APN + * @param username (not used) + * @param password (not used) + * @return nsapi_error_t + */ + nsapi_error_t connect(const char *apn, const char *username, const char *password); + + /** + * Disconnect BG96 from AP + * + * @return true if BG96 is disconnected successfully + */ + bool disconnect(void); + + /** + * Get the IP address of BG96 + * + * @return null-teriminated IP address or null if no IP address is assigned + */ + const char *getIPAddress(char*); + + /** + * Get the MAC address of BG96 + * + * @return null-terminated MAC address or null if no MAC address is assigned + */ + const char *getMACAddress(char*); + + /** + * Check if BG96 is conenected + * + * @return true only if the chip has an IP address + */ + bool isConnected(void); + + /** + * Open a socketed connection + * + * @param type the type of socket to open "u" (UDP) or "t" (TCP) + * @param id for saving socket number to (returned by BG96) + * @param port port to open connection with + * @param addr the IP address of the destination + * @return true only if socket opened successfully + */ + bool open(const char type, int id, const char* addr, int port); + + /** + * Sends data to an open socket + * + * @param id of socket to send to + * @param data to be sent + * @param amount of data to be sent + * @return true only if data sent successfully + */ + bool send(int id, const void *data, uint32_t amount); + + /** + * Receives data from an open socket + * + * @param id to receive from + * @param pointer to data for returned information + * @param amount number of bytes to be received + * @return the number of bytes received + */ + int32_t recv(int, void *, uint32_t); + + /** + * Closes a socket + * + * @param id id of socket to close, valid only 0-4 + * @return true only if socket is closed successfully + */ + bool close(int id); + + /** + * Checks if data is available + */ + bool readable(); + + /** + * Checks if data can be written + */ + bool writeable(); + + /** + * Resolves a URL name to IP address + */ + bool resolveUrl(const char *name, char* str); + + /* + * Obtain or set the current BG96 active context + */ + int setContext( int i ); + + /* + * enable/disable AT command tracing + */ + void doDebug(int f); + + /** Return the BG96 revision info + * + * @param none. + */ + const char* getRev(char*); + + /** Return the last error to occur + * + * @param char* [at least 40 long] + */ + bool getError(char *); + + /** Return the amount a data available + * + * @param char* [at least 40 long] + */ + int rxAvail(int); + + /** Return true/false if rx data is available + * + * @param socket to check + */ + bool chkRxAvail(int id); + +private: + bool tx2bg96(char* cmd); + bool BG96Ready(void); + bool hw_reset(void); + + int _contextID; + Mutex _bg96_mutex; + + UARTSerial _serial; + ATCmdParser _parser; + + DigitalOut _bg96_reset; + DigitalOut _vbat_3v8_en; + DigitalOut _bg96_pwrkey; + +}; + +#endif //__BG96_H__ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/BG96Interface.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,957 @@ +/** +* copyright (c) 2018, James Flynn +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/**---------------------------------------------------------- +* @file BG96Interface.cpp +* @brief BG96 NetworkInterfaceAPI implementation for Mbed OS v5.x +* +* @author James Flynn +* +* @date 1-April-2018 +*/ + +#include <ctype.h> +#include "mbed.h" +#include "BG96.h" +#include "BG96Interface.h" + +// +// The driver uses a simple/basic state machine to manage receiving and transmitting +// data. These are the states that are used +// +#define READ_INIT 10 //initialize for a read state +#define READ_START 11 //start reading from the BG96 +#define READ_ACTIVE 12 //a read is on-going/active +#define READ_DOCB 13 //need to perform a call-back on the socket +#define DATA_AVAILABLE 14 //indicates that rx data is available + +#define TX_IDLE 20 //indicates that no data is bing TX'd +#define TX_STARTING 21 //indicates that TX data is starting +#define TX_ACTIVE 22 //indicates that TX data is being sent +#define TX_COMPLETE 23 //all TX data has been sent +#define TX_DOCB 24 //indicatew we need to exeucte the call-back + +#define BG96_READ_TIMEOUTMS 30000 //read timeout in MS +#define EQ_FREQ 50 //frequency in ms to check for Tx/Rx data +#define EQ_FREQ_SLOW 2000 //frequency in ms to check when in slow monitor mode + +#define EVENT_COMPLETE 0 //signals when a TX/RX event is complete +#define EVENT_GETMORE 0x01 //signals when we need additional TX/RX data + +// +// The following are only used when debug is eabled. +// + +#if MBED_CONF_APP_BG96_DEBUG == true +#define debugOutput(...) _dbOut(__VA_ARGS__) +#define debugDump_arry(...) _dbDump_arry(__VA_ARGS__) + +#define dbgIO_lock dbgout_mutex.lock(); //used to prevent stdio output over-writes +#define dbgIO_unlock dbgout_mutex.unlock(); //when executing--including BG96 debug output + +/** functions to output debug data--------------------------- +* +* @author James Flynn +* @param data pointer to the data array to dump +* @param size number of bytes to dump +* @return void +* @date 1-Feb-2018 +*/ + + +void BG96Interface::_dbDump_arry( const uint8_t* data, unsigned int size ) +{ + unsigned int i, k; + + dbgIO_lock; + if( g_debug & DBGMSG_ARRY ) { + for (i=0; i<size; i+=16) { + printf("[BG96 Driver]:0x%04X: ",i); + for (k=0; k<16; k++) { + if( (i+k)<size ) + printf("%02X ", data[i+k]); + else + printf(" "); + } + printf(" "); + for (k=0; k<16; k++) { + if( (i+k)<size ) + printf("%c", isprint(data[i+k])? data[i+k]:'.'); + } + printf("\n\r"); + } + } + dbgIO_unlock; +} + +void BG96Interface::_dbOut(int who, const char* format, ...) +{ + char buffer[256]; + dbgIO_lock; + if( who & (g_debug & (DBGMSG_DRV|DBGMSG_EQ)) ) { + va_list args; + va_start (args, format); + printf("[BG96 Driver]: "); + vsnprintf(buffer, sizeof(buffer), format, args); + printf("%s",buffer); + printf("\n"); + va_end (args); + } + dbgIO_unlock; +} + +#else + +#define dbgIO_lock +#define dbgIO_unlock +#define debugOutput(...) {/* __VA_ARGS__ */} +#define debugDump_arry(...) {/* __VA_ARGS__ */} + +#endif //MBED_CONF_APP_BG96_DEBUG == true + + +/** -------------------------------------------------------- +* @brief BG96Interface constructor +* @param none +* @retval none +*/ +BG96Interface::BG96Interface(void) : + g_isInitialized(NSAPI_ERROR_NO_CONNECTION), + g_bg96_queue_id(-1), + scheduled_events(0), + _BG96(false) +{ + for( int i=0; i<BG96_SOCKET_COUNT; i++ ) { + g_sock[i].id = -1; + g_sock[i].disTO = false; + g_sock[i].connected = false; + g_socRx[i].m_rx_state = READ_START; + g_socRx[i].m_rx_disTO = false; + g_socTx[i].m_tx_state = TX_IDLE; + } + #if MBED_CONF_APP_BG96_DEBUG == true + g_debug=0; + #endif +} + +/** ---------------------------------------------------------- +* @brief BG96Interface destructor +* @param none +* @retval none +*/ +BG96Interface::~BG96Interface() +{ +} + +/** ---------------------------------------------------------- +* @brief network connect +* connects to Access Point, can be called with no argument +* or arguments. If none, use default APN. +* @param ap: Access Point Name (APN) Name String +* pass_word: Password String for AP +* username: username to use for AP +* @retval NSAPI Error Type +*/ +nsapi_error_t BG96Interface::connect(void) +{ + nsapi_error_t ret = NSAPI_ERROR_OK; + debugOutput(DBGMSG_DRV,"BG96Interface::connect(void) ENTER."); + if( g_isInitialized == NSAPI_ERROR_NO_CONNECTION ) + ret = connect(DEFAULT_APN, NULL, NULL); + + return (ret == NSAPI_ERROR_NO_CONNECTION); +} + +nsapi_error_t BG96Interface::connect(const char *apn, const char *username, const char *password) +{ + Timer t; + bool ok=false; + + debugOutput(DBGMSG_DRV,"BG96Interface::connect(%s,%s,%s) ENTER",apn,username,password); + + if( g_isInitialized == NSAPI_ERROR_IS_CONNECTED ) + ok = disconnect(); + + t.start(); + dbgIO_lock; + while(t.read_ms() < BG96_MISC_TIMEOUT && !ok) + ok = _BG96.startup(); + dbgIO_unlock; + + if( ok && g_bg96_queue_id == -1) + g_bg96_queue_id = _bg96_monitor.start(callback(&_bg96_queue, &EventQueue::dispatch_forever)); + + debugOutput(DBGMSG_DRV,"BG96Interface::connect EXIT"); + + return ok? set_credentials(apn, username, password) : NSAPI_ERROR_DEVICE_ERROR; +} + +/** Set the cellular network credentials -------------------- +* +* @param apn Optional, APN of network +* @param user Optional, username --not used-- +* @param pass Optional, password --not used-- +* @return nsapi_error_t +*/ +nsapi_error_t BG96Interface::set_credentials(const char *apn, const char *username, const char *password) +{ + debugOutput(DBGMSG_DRV,"BG96Interface::set_credentials ENTER/EXIT, APN=%s, USER=%s, PASS=%s",apn,username,password); + g_isInitialized = (_BG96.connect((char*)apn, (char*)username, (char*)password)==true)? NSAPI_ERROR_NO_CONNECTION : NSAPI_ERROR_IS_CONNECTED; + + return g_isInitialized; +} + +/**---------------------------------------------------------- +* @brief network disconnect +* disconnects from APN +* @param none +* @return nsapi_error_t +*/ +int BG96Interface::disconnect(void) +{ + nsapi_error_t ret; + + debugOutput(DBGMSG_DRV,"BG96Interface::disconnect ENTER"); + _bg96_queue.cancel(g_bg96_queue_id); + g_bg96_queue_id = -1; + g_isInitialized = NSAPI_ERROR_NO_CONNECTION; + dbgIO_lock; + ret = _BG96.disconnect(); + dbgIO_unlock; + debugOutput(DBGMSG_DRV,"BG96Interface::disconnect EXIT"); + return ret? NSAPI_ERROR_OK:NSAPI_ERROR_DEVICE_ERROR; +} + +/**---------------------------------------------------------- +* @brief Get the local IP address +* @param none +* @retval Null-terminated representation of the local IP address +* or null if not yet connected +*/ +const char *BG96Interface::get_ip_address() +{ + static char ip[25]; + debugOutput(DBGMSG_DRV,"BG96Interface::get_ip_address ENTER"); + dbgIO_lock; + const char* ptr = _BG96.getIPAddress(ip); + dbgIO_unlock; + + debugOutput(DBGMSG_DRV,"BG96Interface::get_ip_address EXIT"); + return ptr; +} + +/**---------------------------------------------------------- +* @brief Get the MAC address +* @param none +* @retval Null-terminated representation of the MAC address +* or null if not yet connected +*/ +const char *BG96Interface::get_mac_address() +{ + static char mac[25]; + debugOutput(DBGMSG_DRV,"BG96Interface::get_mac_address ENTER"); + dbgIO_lock; + const char* ptr = _BG96.getMACAddress(mac); + dbgIO_unlock; + debugOutput(DBGMSG_DRV,"BG96Interface::get_mac_address EXIT"); + return ptr; +} + +/**---------------------------------------------------------- +* @brief Get Module Firmware Information +* @param none +* @retval Null-terminated representation of the MAC address +* or null if error +*/ +const char* BG96Interface::getRevision(void) +{ + static char str[40]; + dbgIO_lock; + const char* ptr = _BG96.getRev(str); + dbgIO_unlock; + return ptr; +} + +/**---------------------------------------------------------- +* @brief attach function/callback to the socket +* Not used +* @param handle: Pointer to handle +* callback: callback function pointer +* data: pointer to data +* @retval none +*/ +void BG96Interface::socket_attach(void *handle, void (*callback)(void *), void *data) +{ + BG96SOCKET *sock = (BG96SOCKET*)handle; + debugOutput(DBGMSG_DRV,"ENTER/EXIT socket_attach(), socket %d attached",sock->id); + sock->_callback = callback; + sock->_data = data; +} + + +/**---------------------------------------------------------- +* @brief bind to a port number and address +* @param handle: Pointer to socket handle +* proto: address to bind to +* @return nsapi_error_t +*/ +int BG96Interface::socket_bind(void *handle, const SocketAddress &address) +{ + debugOutput(DBGMSG_DRV,"BG96Interface::socket_bind ENTER/EXIT"); + return socket_listen(handle, 1); +} + +/**---------------------------------------------------------- +* @brief start listening on a port and address +* @param handle: Pointer to handle +* backlog: not used (always value is 1) +* @return nsapi_error_t +*/ +int BG96Interface::socket_listen(void *handle, int backlog) +{ + BG96SOCKET *socket = (BG96SOCKET *)handle; + nsapi_error_t ret = NSAPI_ERROR_OK; + + backlog = backlog; //avoid unused error from compiler + debugOutput(DBGMSG_DRV,"BG96Interface::socket_listen, socket %d listening %s ENTER", + socket->id, socket->connected? "YES":"NO"); + if( !socket->connected ) { + socket->disTO = true; + _eq_schedule(); + } + else + ret = NSAPI_ERROR_NO_CONNECTION; + + debugOutput(DBGMSG_DRV,"BG96Interface::socket_listen EXIT"); + return ret; +} + +/**---------------------------------------------------------- +* @brief Set the socket options +* Not used +* @param handle: Pointer to handle +* level: SOL_SOCKET +* optname: option name +* optval: pointer to option value +* optlen: option length +* @return nsapi_error_t +*/ +int BG96Interface::setsockopt(void *handle, int level, int optname, const void *optval, unsigned optlen) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + + debugOutput(DBGMSG_DRV,"BG96Interface::setsockopt ENTER/EXIT"); + if (!optlen || !sock) { + return NSAPI_ERROR_PARAMETER; + } + + if (level == NSAPI_SOCKET && sock->proto == NSAPI_TCP) { + switch (optname) { + case NSAPI_REUSEADDR: + case NSAPI_KEEPIDLE: + case NSAPI_KEEPINTVL: + case NSAPI_LINGER: + case NSAPI_SNDBUF: + case NSAPI_ADD_MEMBERSHIP: + case NSAPI_DROP_MEMBERSHIP: + case NSAPI_KEEPALIVE: + return NSAPI_ERROR_UNSUPPORTED; + + case NSAPI_RCVBUF: + if (optlen == sizeof(void *)) { + sock->dptr_last = (void*)optval; + sock->dptr_size = (unsigned)optlen; + return NSAPI_ERROR_OK; + } + return NSAPI_ERROR_PARAMETER; + } + } + return NSAPI_ERROR_UNSUPPORTED; +} + +/**---------------------------------------------------------- +* @brief Get the socket options +* Not used +* @param handle: Pointer to handle +* level: SOL_SOCKET +* optname: option name +* optval: pointer to option value +* optlen: pointer to option length +* @return nsapi_error_t +*/ +int BG96Interface::getsockopt(void *handle, int level, int optname, void *optval, unsigned *optlen) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + + debugOutput(DBGMSG_DRV,"BG96Interface::getsockopt ENTER/EXIT"); + if (!optval || !optlen || !sock) { + return NSAPI_ERROR_PARAMETER; + } + + if (level == NSAPI_SOCKET && sock->proto == NSAPI_TCP) { + switch (optname) { + case NSAPI_REUSEADDR: + case NSAPI_KEEPALIVE: + case NSAPI_KEEPIDLE: + case NSAPI_KEEPINTVL: + case NSAPI_LINGER: + case NSAPI_SNDBUF: + case NSAPI_ADD_MEMBERSHIP: + case NSAPI_DROP_MEMBERSHIP: + return NSAPI_ERROR_UNSUPPORTED; + + case NSAPI_RCVBUF: + optval = sock->dptr_last; + *optlen = sock->dptr_size; + return NSAPI_ERROR_OK; + } + } + return NSAPI_ERROR_UNSUPPORTED; +} + +/**---------------------------------------------------------- +* @brief helpe function to set debug levels. Only enabled +* if debug flag set during compilation +* @param int = value to set debug flag to +* @retval none +*/ +void BG96Interface::doDebug( int v ) +{ + #if MBED_CONF_APP_BG96_DEBUG == true + gvupdate_mutex.lock(); + _BG96.doDebug(v); + g_debug= v; + gvupdate_mutex.unlock(); + debugOutput(DBGMSG_DRV,"SET debug flag to 0x%02X",v); + #endif +} + +/**---------------------------------------------------------- +* @brief open a socket handle +* @param handle: Pointer to handle +* proto: TCP/UDP protocol +* @return nsapi_error_t +*/ +int BG96Interface::socket_open(void **handle, nsapi_protocol_t proto) +{ + int i; + nsapi_error_t ret=NSAPI_ERROR_OK; + + debugOutput(DBGMSG_DRV,"ENTER socket_open(), protocol=%s", (proto==NSAPI_TCP)?"TCP":"UDP"); + gvupdate_mutex.lock(); + for( i=0; i<BG96_SOCKET_COUNT; i++ ) //find the next available socket... + if( g_sock[i].id == -1 ) + break; + + if( i == BG96_SOCKET_COUNT ) { + ret = NSAPI_ERROR_NO_SOCKET; + debugOutput(DBGMSG_DRV,"EXIT socket_open; NO SOCKET AVAILABLE (%d)",i); + } + else{ + debugOutput(DBGMSG_DRV,"socket_open using socket %d", i); + + g_socTx[i].m_tx_state = TX_IDLE; + g_socRx[i].m_rx_state = READ_START; + + g_sock[i].id = i; + g_sock[i].disTO = false; + g_sock[i].proto = proto; + g_sock[i].connected = false; + g_sock[i]._callback = NULL; + g_sock[i]._data = NULL; + *handle = &g_sock[i]; + debugOutput(DBGMSG_DRV,"EXIT socket_open; Socket=%d, protocol =%s", + i, (g_sock[i].proto==NSAPI_UDP)?"UDP":"TCP"); + } + gvupdate_mutex.unlock(); + + return ret; +} + +/**---------------------------------------------------------- +* @brief close a socket +* @param handle: Pointer to handle +* @return nsapi_error_t +*/ +int BG96Interface::socket_close(void *handle) +{ + BG96SOCKET *sock = (BG96SOCKET*)handle; + nsapi_error_t ret =NSAPI_ERROR_DEVICE_ERROR; + RXEVENT *rxsock; + TXEVENT *txsock; + int i = sock->id; + + debugOutput(DBGMSG_DRV,"ENTER socket_close(); Socket=%d", i); + + if(i >= 0) { + txrx_mutex.lock(); + txsock = &g_socTx[i]; + rxsock = &g_socRx[i]; + + txsock->m_tx_state = TX_IDLE; + rxsock->m_rx_state = READ_START; + + dbgIO_lock; + if( sock->connected ) + _BG96.close(sock->id); + dbgIO_unlock; + + sock->id = -1; + sock->disTO = false; + sock->proto = NSAPI_TCP; + sock->connected= false; + sock->_callback= NULL; + sock->_data = NULL; + ret = NSAPI_ERROR_OK; + txrx_mutex.unlock(); + debugOutput(DBGMSG_DRV,"EXIT socket_close(), socket %d - success",i); + } + else + debugOutput(DBGMSG_DRV,"EXIT socket_close() - fail"); + return ret; +} + +/**---------------------------------------------------------- +* @brief accept connections from remote sockets +* @param handle: Pointer to handle of client socket (connecting) +* proto: handle of server socket which will accept connections +* @return nsapi_error_t +*/ +int BG96Interface::socket_accept(nsapi_socket_t server,nsapi_socket_t *handle, SocketAddress *address) +{ + return NSAPI_ERROR_UNSUPPORTED; +} + +/**---------------------------------------------------------- +* @brief connect to a remote socket +* @param handle: Pointer to socket handle +* addr: Address to connect to +* @return nsapi_error_t +*/ +int BG96Interface::socket_connect(void *handle, const SocketAddress &addr) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + nsapi_error_t ret=NSAPI_ERROR_OK; + const char proto = (sock->proto == NSAPI_UDP) ? 'u' : 't'; + bool k; + int cnt; + + + debugOutput(DBGMSG_DRV,"ENTER socket_connect(); Socket=%d; IP=%s; PORT=%d;", + sock->id, addr.get_ip_address(), addr.get_port()); + dbgIO_lock; + for( k=true, cnt=0; cnt<3 && k; cnt++ ) { + k = !_BG96.open(proto, sock->id, addr.get_ip_address(), addr.get_port()); + if( k ) + _BG96.close(sock->id); + } + dbgIO_unlock; + + if( cnt<3 ) { + sock->addr = addr; + sock->connected = true; + + if( sock->_callback != NULL ) + sock->_callback(sock->_data); + } + else + ret = NSAPI_ERROR_DEVICE_ERROR; + + debugOutput(DBGMSG_DRV,"EXIT socket_connect(), Socket %d",sock->id); + return ret; +} + +/**---------------------------------------------------------- +* @brief return the address of this object +* @param none +* @retval pointer to this class object +*/ +NetworkStack *BG96Interface::get_stack() +{ + return this; +} + +/**---------------------------------------------------------- +* @brief return IP address after looking up the URL name +* @param name = URL string +* address = address to store IP in +* version = not used +* @return nsapi_error_t +*/ +nsapi_error_t BG96Interface::gethostbyname(const char* name, SocketAddress *address, nsapi_version_t version) +{ + char ipstr[25]; + bool ok; + nsapi_error_t ret=NSAPI_ERROR_OK; + + debugOutput(DBGMSG_DRV,"ENTER gethostbyname(); IP=%s; PORT=%d; URL=%s;", + address->get_ip_address(), address->get_port(), name); + + dbgIO_lock; + ok=_BG96.resolveUrl(name,ipstr); + dbgIO_unlock; + + if( !ok ) { + ret = NSAPI_ERROR_DEVICE_ERROR; + debugOutput(DBGMSG_DRV,"EXIT gethostbyname() -- failed to get DNS"); + } + else{ + address->set_ip_address(ipstr); + debugOutput(DBGMSG_DRV,"EXIT gethostbyname(); IP=%s; PORT=%d; URL=%s;", + address->get_ip_address(), address->get_port(), name); + } + return ret; +} + +/**---------------------------------------------------------- +* @brief send data to a udp socket +* @param handle: Pointer to handle +* addr: address of udp socket +* data: pointer to data +* size: size of data +* @retval no of bytes sent +*/ +int BG96Interface::socket_sendto(void *handle, const SocketAddress &addr, const void *data, unsigned size) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + int err=NSAPI_ERROR_OK; + + if (!sock->connected) + err = socket_connect(sock, addr); + + if( err != NSAPI_ERROR_OK ) + return err; + else + return socket_send(sock, data, size); +} + + +/**---------------------------------------------------------- +* @brief write to a socket +* @param handle: Pointer to handle +* data: pointer to data +* size: size of data +* @retval no of bytes sent +*/ +int BG96Interface::socket_send(void *handle, const void *data, unsigned size) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + TXEVENT *txsock; + + debugOutput(DBGMSG_DRV,"ENTER socket_send(),socket %d, send %d bytes",sock->id,size); + + if( size < 1 || data == NULL ) // should never happen but have seen it + return 0; + + txrx_mutex.lock(); + txsock = &g_socTx[sock->id]; + + switch( txsock->m_tx_state ) { + case TX_IDLE: + txsock->m_tx_socketID = sock->id; + txsock->m_tx_state = TX_STARTING; + txsock->m_tx_dptr = (uint8_t*)data; + txsock->m_tx_orig_size = size; + txsock->m_tx_req_size = (uint32_t)size; + txsock->m_tx_total_sent= 0; + txsock->m_tx_callback = sock->_callback; + txsock->m_tx_cb_data = sock->_data; + debugDump_arry((const uint8_t*)data,size); + + if( txsock->m_tx_req_size > BG96::BG96_BUFF_SIZE ) + txsock->m_tx_req_size= BG96::BG96_BUFF_SIZE; + + if( tx_event(txsock) != EVENT_COMPLETE ) { //if we didn't sent all the data, schedule background send the rest + debugOutput(DBGMSG_DRV,"Schedule TX event for socket %d",sock->id); + txsock->m_tx_state = TX_ACTIVE; + _eq_schedule(); + txrx_mutex.unlock(); + return NSAPI_ERROR_WOULD_BLOCK; + } + // fall through + + if( txsock->m_tx_state == TX_DOCB ) { + debugOutput(DBGMSG_DRV,"Call socket %d TX call-back",sock->id); + txsock->m_tx_state = TX_COMPLETE; + txsock->m_tx_callback( txsock->m_tx_cb_data ); + } + + // fall through + + case TX_COMPLETE: + debugOutput(DBGMSG_DRV,"EXIT socket_send(), socket %d, sent %d bytes", txsock->m_tx_socketID,txsock->m_tx_total_sent); + txsock->m_tx_state = TX_IDLE; + txrx_mutex.unlock(); + return txsock->m_tx_total_sent; + + case TX_ACTIVE: + case TX_STARTING: + debugOutput(DBGMSG_DRV,"EXIT socket_send(), TX_ACTIVE/TX_STARTING"); + txrx_mutex.unlock(); + return NSAPI_ERROR_WOULD_BLOCK; + + case TX_DOCB: + default: + debugOutput(DBGMSG_DRV,"EXIT socket_send(), NSAPI_ERROR_DEVICE_ERROR"); + txrx_mutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } +} + +/**---------------------------------------------------------- +* @brief receive data on a udp socket +* @param handle: Pointer to handle +* addr: address of udp socket +* data: pointer to data +* size: size of data +* @retval no of bytes read +*/ +int BG96Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *data, unsigned size) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + + if (!sock->connected) + return NSAPI_ERROR_NO_CONNECTION; + *addr = sock->addr; + return socket_recv(sock, data, size); +} + +/**---------------------------------------------------------- +* @brief receive data on a socket +* @param handle: Pointer to socket handle +* data: pointer to data +* size: size of data +* @retval no of bytes read +*/ +int BG96Interface::socket_recv(void *handle, void *data, unsigned size) +{ + BG96SOCKET *sock = (BG96SOCKET *)handle; + RXEVENT *rxsock; + + if( size < 1 || data == NULL ) // should never happen + return 0; + + txrx_mutex.lock(); + rxsock = &g_socRx[sock->id]; + debugOutput(DBGMSG_DRV,"ENTER socket_recv(), socket %d, request %d bytes",sock->id, size); + + switch( rxsock->m_rx_state ) { + case READ_START: //need to start a read sequence of events + rxsock->m_rx_disTO = sock->disTO; + rxsock->m_rx_socketID = sock->id; + rxsock->m_rx_state = READ_INIT; + rxsock->m_rx_dptr = (uint8_t*)data; + rxsock->m_rx_req_size = (uint32_t)size; + rxsock->m_rx_total_cnt = 0; + rxsock->m_rx_timer = 0; + rxsock->m_rx_return_cnt= 0; + + if( rxsock->m_rx_req_size > BG96::BG96_BUFF_SIZE) + rxsock->m_rx_req_size= BG96::BG96_BUFF_SIZE; + + rxsock->m_rx_callback = sock->_callback; + rxsock->m_rx_cb_data = sock->_data; + // fall through + if( rx_event(rxsock) != EVENT_COMPLETE ){ + rxsock->m_rx_state = READ_ACTIVE; + _eq_schedule(); + debugOutput(DBGMSG_DRV,"EXIT socket_recv, scheduled read of socket %d.", sock->id); + txrx_mutex.unlock(); + return NSAPI_ERROR_WOULD_BLOCK; + } + + //got data, fall thru and finish. no need to schedule the background task + if( rxsock->m_rx_state == READ_DOCB ) { + debugOutput(DBGMSG_DRV,"Call socket %d RX call-back",sock->id); + rxsock->m_rx_state = DATA_AVAILABLE; + rxsock->m_rx_callback( rxsock->m_rx_cb_data ); + } + + // fall through + + case DATA_AVAILABLE: + debugOutput(DBGMSG_DRV,"EXIT socket_recv(),socket %d, return %d bytes",sock->id, rxsock->m_rx_return_cnt); + debugDump_arry((const uint8_t*)data,rxsock->m_rx_return_cnt); + rxsock->m_rx_state = READ_START; + txrx_mutex.unlock(); + return rxsock->m_rx_return_cnt; + + case READ_ACTIVE: + case READ_INIT: + debugOutput(DBGMSG_DRV,"EXIT socket_recv(), socket id %d, READ_ACTIVE/INIT", sock->id); + txrx_mutex.unlock(); + return NSAPI_ERROR_WOULD_BLOCK; + + case READ_DOCB: + default: + debugOutput(DBGMSG_DRV,"EXIT socket_recv(), NSAPI_ERROR_DEVICE_ERROR"); + txrx_mutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } +} + +/**---------------------------------------------------------- +* @brief check for and retrieve data user requested. Time out +* after TO period unless socket has TO disabled. +* @param pointer to an RXEVENT +* @retval 1 if need to schedule another check, 0 if data received or Timed Out +*/ +int BG96Interface::rx_event(RXEVENT *ptr) +{ + debugOutput(DBGMSG_EQ,"ENTER rx_event() for socket id %d, size=%d", ptr->m_rx_socketID, ptr->m_rx_req_size); + dbgIO_lock; + int cnt = _BG96.recv(ptr->m_rx_socketID, ptr->m_rx_dptr, ptr->m_rx_req_size); + dbgIO_unlock; + + if( cnt == NSAPI_ERROR_DEVICE_ERROR ) { + debugOutput(DBGMSG_EQ,"EXIT rx_event(), error reading socket %d", ptr->m_rx_socketID); + ptr->m_rx_timer=0; + return EVENT_GETMORE; + } + + if( cnt>0 ) { //got data, return it to the caller + debugOutput(DBGMSG_EQ,"EXIT rx_event(), socket %d received %d bytes", ptr->m_rx_socketID, cnt); + ptr->m_rx_return_cnt += cnt; + ptr->m_rx_state = DATA_AVAILABLE; + if( ptr->m_rx_callback != NULL ) + ptr->m_rx_state = READ_DOCB; + return EVENT_COMPLETE; + } + + if( ++ptr->m_rx_timer > (BG96_READ_TIMEOUTMS/EQ_FREQ) && !ptr->m_rx_disTO ) { //timed out waiting, return 0 to caller + debugOutput(DBGMSG_EQ,"EXIT rx_event(), socket id %d, rx data TIME-OUT!",ptr->m_rx_socketID); + ptr->m_rx_state = DATA_AVAILABLE; + ptr->m_rx_return_cnt = 0; + if( ptr->m_rx_callback != NULL ) + ptr->m_rx_state = READ_DOCB; + return EVENT_COMPLETE; + } + + debugOutput(DBGMSG_EQ,"EXIT rx_event(), socket id %d, sechedule for more.", + ptr->m_rx_socketID); + return EVENT_GETMORE; +} + +/**---------------------------------------------------------- +* @brief send data, if more data than BG96 can handle at one +* send as much as possible, and schedule another event +* @param pointer to TXEVENT structure +* @retval 1 if need to schedule another event, 0 if data sent +*/ +int BG96Interface::tx_event(TXEVENT *ptr) +{ + debugOutput(DBGMSG_EQ,"ENTER tx_event(), socket id %d",ptr->m_tx_socketID); + + dbgIO_lock; + bool done =_BG96.send(ptr->m_tx_socketID, ptr->m_tx_dptr, ptr->m_tx_req_size); + dbgIO_unlock; + + if( done ) + ptr->m_tx_total_sent += ptr->m_tx_req_size; + else{ + debugOutput(DBGMSG_EQ,"EXIT tx_event(), socket id %d, sent no data!",ptr->m_tx_socketID); + return EVENT_GETMORE; + } + + if( ptr->m_tx_total_sent < ptr->m_tx_orig_size ) { + ptr->m_tx_dptr += ptr->m_tx_req_size; + ptr->m_tx_req_size = ptr->m_tx_orig_size-ptr->m_tx_total_sent; + + if( ptr->m_tx_req_size > BG96::BG96_BUFF_SIZE) + ptr->m_tx_req_size= BG96::BG96_BUFF_SIZE; + + debugOutput(DBGMSG_EQ,"EXIT tx_event(), need to send %d more bytes.",ptr->m_tx_req_size); + return EVENT_GETMORE; + } + debugOutput(DBGMSG_EQ,"EXIT tx_event, socket id %d, sent %d bytes",ptr->m_tx_socketID,ptr->m_tx_total_sent); + ptr->m_tx_state = TX_COMPLETE; + if( ptr->m_tx_callback != NULL ) + ptr->m_tx_state = TX_DOCB; + + return EVENT_COMPLETE; +} + + +/**---------------------------------------------------------- +* @brief periodic event(EventQueu thread) to check for RX and TX data. If checking for RX data with TO disabled +* slow down event checking after a while. +* @param none +* @retval none +*/ +void BG96Interface::g_eq_event(void) +{ + int done = txrx_mutex.trylock(); + bool goSlow = false; + + if( scheduled_events > 0 ) + scheduled_events--; + + if( !done ) { + _eq_schedule(); + return; + } + + done = EVENT_COMPLETE; + for( unsigned int i=0; i<BG96_SOCKET_COUNT; i++ ) { + if( g_socRx[i].m_rx_state == READ_ACTIVE || g_socRx[i].m_rx_disTO) { + done |= rx_event(&g_socRx[i]); + goSlow |= ( g_socRx[i].m_rx_timer > ((BG96_READ_TIMEOUTMS/EQ_FREQ)*(EQ_FREQ_SLOW/EQ_FREQ)) ); + + if( goSlow ) + g_socRx[i].m_rx_timer = (BG96_READ_TIMEOUTMS/EQ_FREQ)*(EQ_FREQ_SLOW/EQ_FREQ); + } + + if( g_socTx[i].m_tx_state == TX_ACTIVE ) { + goSlow = false; + done |= tx_event(&g_socTx[i]); + } + } + + for( unsigned int i=0; i<BG96_SOCKET_COUNT; i++ ) { + if( g_socRx[i].m_rx_state == READ_DOCB ) { + debugOutput(DBGMSG_EQ,"Call socket %d RX call-back",i); + g_socRx[i].m_rx_state = DATA_AVAILABLE; + g_socRx[i].m_rx_callback( g_socRx[i].m_rx_cb_data ); + } + + if( g_socTx[i].m_tx_state == TX_DOCB ) { + debugOutput(DBGMSG_EQ,"Call socket %d TX call-back",i); + g_socTx[i].m_tx_state = TX_COMPLETE; + g_socTx[i].m_tx_callback( g_socTx[i].m_tx_cb_data ); + } + } + + if( done != EVENT_COMPLETE ) + _eq_schedule(); + + debugOutput(DBGMSG_EQ, "EXIT eq_event, queue=%d\n", scheduled_events); + txrx_mutex.unlock(); +} + + +void BG96Interface::_eq_schedule(void) +{ + if( scheduled_events < BG96_SOCKET_COUNT ) { + scheduled_events++; + _bg96_queue.call_in(EQ_FREQ,mbed::Callback<void()>((BG96Interface*)this,&BG96Interface::g_eq_event)); + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/BG96Interface.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,346 @@ +/** +* copyright (c) 2018, James Flynn +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** +* @file BG96Interface.h +* @brief Implements NetworkInterface class for use with the Quectel BG96 +* data module running MBed OS v5.x +* +* @author James Flynn +* +* @date 1-April-2018 +*/ + +#ifndef __BG96Interface_H__ +#define __BG96Interface_H__ + +#include <stdint.h> + +#include "mbed.h" +#include "Callback.h" + +#include "BG96.h" + +#define APN_DEFAULT "m2m.com.attz" +#define BG96_MISC_TIMEOUT 15000 +#define BG96_SOCKET_COUNT 5 + +#define DBGMSG_DRV 0x04 +#define DBGMSG_EQ 0x08 +#define DBGMSG_ARRY 0x20 + +#define FIRMWARE_REV(x) (((BG96Interface*)x)->getRevision()) + +typedef struct rx_event_t { + int m_rx_state; //state of the socket receive + int m_rx_socketID; //which socket is being rcvd on + uint8_t *m_rx_dptr; //pointer to the users data buffer + uint32_t m_rx_req_size; //Requested number of bytes to receive + uint32_t m_rx_asked_size; + uint32_t m_rx_total_cnt; //Total number of bytes received + int m_rx_timer; //Rx Timeout Timer + int m_rx_disTO; //Flag to disable Timeout Timer + void (*m_rx_callback)(void*);//callback used with attach + void *m_rx_cb_data; //callback data to be returned + uint32_t m_rx_return_cnt; //number of bytes the Event Queue is returning + } RXEVENT; + +typedef struct tx_event_t { + int m_tx_state; + int m_tx_socketID; + uint8_t *m_tx_dptr; + unsigned m_tx_orig_size; + uint32_t m_tx_req_size; + uint32_t m_tx_total_sent; + void (*m_tx_callback)(void*); + void *m_tx_cb_data; + } TXEVENT; + + +/** BG96_socket class + * Implementation of BG96 socket structure + */ +typedef struct _socket_t { + int id; //nbr given by BG96 driver or -1 if not used + SocketAddress addr; //address this socket is attached to + bool disTO; //true of socket is listening for incomming data + nsapi_protocol_t proto; //TCP or UDP + bool connected; //true if socket is connected + void (*_callback)(void*); //callback used with attach + void *_data; //callback data to be returned + void *dptr_last; //pointer to the last data buffer used + unsigned dptr_size; //the size of the last user data buffer + } BG96SOCKET; + + +class BG96Interface : public NetworkStack, public NetworkInterface +{ +public: + BG96Interface(); + virtual ~BG96Interface(); + + /** Connect to the network (no parameters) + * + * @return nsapi_error_t + */ + virtual nsapi_error_t connect(void); + + /** Connect to the network + * + * @param apn Optional, APN of network + * @param user Optional, username --not used-- + * @param pass Optional, password --not used-- + * @return nsapi_error_t + */ + virtual nsapi_error_t connect(const char *apn, const char *username = 0, const char *password = 0); + + /** Set the cellular network credentials + * + * @param apn Optional, APN of network + * @param user Optional, username --not used-- + * @param pass Optional, password --not used-- + * @return nsapi_error_t + */ + virtual nsapi_error_t set_credentials(const char *apn = 0, + const char *username = 0, const char *password = 0); + + /** disconnect from the network + * + * @return nsapi_error_t + */ + virtual nsapi_error_t disconnect(); + + /** Get the IP address of WNC device. From NetworkStack Class + * + * @return IP address string or null + */ + virtual const char *get_ip_address(); + + /** Get the MAC address of the WNC device. + * + * @return MAC address of the interface + */ + virtual const char *get_mac_address(); + + /** Query Module SW revision + * + * @return SW Revision string + */ + const char* getRevision(void); + + /** Query registered state + * + * @return true if registerd, false if not + */ + bool registered(); + + /** Set the level of Debug output + * + * @param bit field + * mbed driver info = 0x04 + * dump buffers = 0x20 + * AT command tracing = 0x80 + */ + void doDebug( int v ); + + +protected: + + /** Get Host IP by name. + * + * @return nsapi_error_t + */ + virtual nsapi_error_t gethostbyname(const char* name, SocketAddress *address, nsapi_version_t version); + + + /** return a pointer to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack(void); + + /** Open a socket. + * + * @param handle Handle in which to store new socket + * @param proto Type of socket to open, NSAPI_TCP or NSAPI_UDP + * @return nsapi_error_t + */ + virtual int socket_open(void **handle, nsapi_protocol_t proto); + + + /* setsockopt allows applications to pass stack-specific hints + * to the underlying stack. For unsupported options, + * NSAPI_ERROR_UNSUPPORTED is returned and the socket is unmodified. + * + * @param handle Socket handle + * @param level Stack-specific protocol level + * @param optname Stack-specific option identifier + * @param optval Option value + * @param optlen Length of the option value + * @return nsapi_error_t + */ + virtual nsapi_error_t setsockopt(nsapi_socket_t handle, int level, int optname, const void *optval, unsigned optlen); + + + /* getsockopt retrieves stack-specific options. + * + * unsupported options return NSAPI_ERROR_UNSUPPORTED + * + * @param level Stack-specific protocol level or nsapi_socket_level_t + * @param optname Level-specific option name + * @param optval Destination for option value + * @param optlen Length of the option value + * @return nsapi_error_t + */ + virtual nsapi_error_t getsockopt(nsapi_socket_t handle, int level, int optname, void *optval, unsigned *optlen); + + + /** Close the socket. + * + * @param handle Socket handle + * @return 0 on success, negative on failure + */ + virtual int socket_close(void *handle); + + /** Bind a server socket to a specific port. + * + * @brief Bind the socket to a specific port + * @param handle Socket handle + * @param address address to listen for + * @return 0; + */ + virtual int socket_bind(void *handle, const SocketAddress &address); + + /** Start listening for incoming connections. + * + * @brief NOT SUPPORTED + * @param handle Socket handle + * @param backlog Number of pending connections that can be queued up at any + * one time [Default: 1] + * @return nsapi_error_t + */ + virtual nsapi_error_t socket_listen(void *handle, int backlog); + + /** Accept a new connection. + * + * @brief NOT SUPPORTED + * @return NSAPI_ERROR_UNSUPPORTED; + */ + virtual int socket_accept(nsapi_socket_t server, + nsapi_socket_t *handle, SocketAddress *address=0); + + /** Connects this socket to the server. + * + * @param handle Socket handle + * @param address SocketAddress + * @return nsapi_error_t + */ + virtual int socket_connect(void *handle, const SocketAddress &address); + + /** Send data to the remote host. + * + * @param handle Socket handle + * @param data buffer to send + * @param size length of buffer + * @return Number of bytes written or negative on failure + * + * @note This call is non-blocking. + */ + virtual int socket_send(void *handle, const void *data, unsigned size); + + /** Receive data from the remote host. + * + * @param handle Socket handle + * @param data buffer to store the recived data + * @param size bytes to receive + * @return received bytes received, negative on failure + * + * @note This call is non-blocking. + */ + virtual int socket_recv(void *handle, void *data, unsigned size); + + /** Send a packet to a remote endpoint. + * + * @param handle Socket handle + * @param address SocketAddress + * @param data data to send + * @param size number of bytes to send + * @return the number of bytes sent or negative on failure + * + * @note This call is non-blocking + */ + virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size); + + /** Receive packet remote endpoint + * + * @param handle Socket handle + * @param address SocketAddress + * @param buffer buffer to store data to + * @param size number of bytes to receive + * @return the number bytes received or negative on failure + * + * @note This call is non-blocking + */ + virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size); + + /** Register a callback on state change of the socket + * + * @param handle Socket handle + * @param callback Function to call on state change + * @param data Argument to pass to callback + * + * @note Callback may be called in an interrupt context. + */ + virtual void socket_attach(void *handle, void (*callback)(void *), void *data); + +private: + + int tx_event(TXEVENT *ptr); //called to TX data + int rx_event(RXEVENT *ptr); //called to RX data + void g_eq_event(void); //event queue to tx/rx + void _eq_schedule(void); + + nsapi_error_t g_isInitialized; //TRUE if the BG96Interface is connected to the network + int g_bg96_queue_id; //the ID of the EventQueue used by the driver + uint32_t scheduled_events; + + BG96SOCKET g_sock[BG96_SOCKET_COUNT]; // + TXEVENT g_socTx[BG96_SOCKET_COUNT]; // + RXEVENT g_socRx[BG96_SOCKET_COUNT]; // + + Thread _bg96_monitor; //event queue thread + EventQueue _bg96_queue; + + Mutex gvupdate_mutex; //protect global variable updates + Mutex txrx_mutex; //protect RX/TX event queue activities + BG96 _BG96; //create the BG96 HW interface object + + #if MBED_CONF_APP_BG96_DEBUG == true + Mutex dbgout_mutex; + int g_debug; //flag for debug settings + void _dbDump_arry( const uint8_t* data, unsigned int size ); + void _dbOut(int, const char *format, ...); + #endif + +}; + +#endif /* __BG96Interface_H__ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/README.md Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,84 @@ +# Mbed OS v5.x Network Interface driver for Quectel BG96 + +## Specifying library configuration and settings + +The following settings configure the I/O settings for the BG96 Driver. The +current settings are for the AVNET RSR1157 NbIOT BG96 expansion board. If you +use a different board, the settings maydiffer. + +```json +{ + "name": "BG96_library", + "config": { + "bg96_reset": { + "help": "RESET pin for the BG96", + "value": "D7" + }, + "bg96_wake": { + "help": "Wake pin to BG96", + "value": "D11" + }, + "bg96_pwrkey": { + "help": "pwr key input to BG96", + "value": "D10" + }, + "bg96_tx": { + "help": "TX pin for serial connection to BG96", + "value": "D8" + }, + "bg96_rx": { + "help": "RX pin for serial connection to BG96", + "value": "D2" + } + } +} +``` + +## Specifing the application configuration settings + +In the mbed_app.json file that is used with this driver, the following settings are +needed. + +```json +{ + "macros": ["DEFAULT_APN=\"m2m.com.attz\"" + ], + "config": { + "bg96_debug": { + "help" : "enable or disable WNC debug messages.", + "value": 0 + }, + "bg96-debug-setting": { + "help" : "bit value 1 and/or 2 enable WncController debug output, bit value 4 enables mbed driver debug output.", + "value": "0x84" + } + }, + "target_overrides": { + "*": { + "platform.stdio-baud-rate": 115200, + "platform.stdio-convert-newlines": true + } + } +} +``` +### Default APN + +The macro DEFAULT_APN is the APN that will be used when the connect method is called with no argments (if you +don't specify an APN in your code). + +### Debug settings + +The bg96_debug setting enables or disabled debug output from the driver. + +The bg96-debug-settings allow you to select various types of debug output. It is a bit-field with the following settings: + + 0x04 - Driver Enter/Exit debug information + 0x08 - Driver Event Queue debug information + 0x20 - Dump Driver Arrays + 0x80 - Trace AT command execution + +### Overriding settings + +The driver is designed to use a baud rater of 115200 so this must be specified (it will default to 9600 otherwise). The +convert-newlines helps terminal output format by removing the requirment to modify the terminal program settings. +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/BG96-driver/mbed_lib.json Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,39 @@ +{ + "name": "bg96-library", + "config": { + "bg96-debug": { + "help" : "enable or disable BG96 debug messages.", + "value": "true" + }, + "bg96-debug_setting": { + "help" : "bit value 1 and/or 2 enable debug output, bit value 4 enables mbed driver debug output.", + "value": "0x84" + }, + "bg96-tx": { + "help": "TX pin for serial connection to BG96 on the NUCLEO Board", + "value": "D8" + }, + "bg96-rx": { + "help": "RX pin for serial connection to BG96 on the NUCLEO Board", + "value": "D2" + }, + "bg96-reset": { + "help": "BG96 Reset pin", + "value": "D7" + }, + "bg96-wake": { + "help": "BG96 Wake pin", + "value": "D11" + }, + "bg96-pwrkey": { + "help": "BG96 PWRKEY pin", + "value": "D10" + } + }, + "target_overrides": { + "K64F": { + "bg96-tx": "D1", + "bg96-rx": "D0" + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/README.md Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,69 @@ +# EZ Connect - A derivative of ARM's Easy-Connect to easily add supported for Avnet connectivity solutions to your mbed OS project + + +Give your application the ability to switch between connectivity methods. The NetworkInterface API makes this easy, but you need a mechanism for the user to chooce the method. EZ Connect handles all of this for you. Just declare the desired connectivity method in the mbed_app.json file and call easy_connect() from your application. + +## Specifying the connectivity method + +Add the following to your `mbed_app.json` file: + +```json +{ + "config": { + "network-interface":{ + "help": "options are ETHERNET, CELLULAR_WNC14A2A, CELLULAR_BG96", + "value": "ETHERNET" + } + } +} +``` + + +## Using EZ Connect from your application + +EZ Connect has just one function that returns either a `NetworkInterface`-pointer or `NULL`: + +```cpp +#include "easy-connect.h" + +int main(int, char**) { + NetworkInterface* network = easy_connect(true); /* has 1 argument, enable_logging (pass in true to log to serial port) */ + if (!network) { + printf("Connecting to the network failed... See serial output.\r\n"); + return 1; + } + + // Rest of your program +} +``` +## BG96 overrides +If you need to change the pins used by the BG96 driver, you can enter the following into your `mbed_app.json`: + +```json + "target_overrides": { + "*": { + "platform.bg96-library.bg96-tx": "D8", + "platform.bg96-library.bg96-rx": "D2", + "platform.bg96-library.bg96-reset": "D7", + "platform.bg96-library.bg96-wake": "D11", + "platform.bg96-library.bg96-pwrkey": "D10" + } + } +``` + +## CR/LF in serial output + +If you want to avoid using `\r\n` in your printouts and just use normal C style `\n` instead, please specify these to your `mbed_app.json`: + +```json + "target_overrides": { + "*": { + "platform.stdio-baud-rate": 115200, + "platform.stdio-convert-newlines": true + } + } +``` + +## Extra defines + +If you'd like to use EZ Connect with mbed Client then you're in luck. EZ Connect automatically defines the `MBED_SERVER_ADDRESS` macro depending on your connectivity method (either IPv4 or IPv6 address). Use this address to connect to the right instance of mbed Device Connector.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/easy-connect.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,157 @@ + +/* + * FILE: easy-connect.cpp + * + * Copyright (c) 2015 - 2017 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mbed.h" +#include "easy-connect.h" + +/* + * Instantiate the configured network interface + */ + +#if MBED_CONF_APP_NETWORK_INTERFACE == ETHERNET +#include "EthernetInterface.h" +EthernetInterface eth; + +#elif MBED_CONF_APP_NETWORK_INTERFACE == CELLULAR_WNC14A2A +#include "WNC14A2AInterface.h" + +#if MBED_CONF_APP_WNC_DEBUG == true +#include "WNCDebug.h" +WNCDebug dbgout(stderr); +WNC14A2AInterface wnc(&dbgout); +#else +WNC14A2AInterface wnc; +#endif + +#elif MBED_CONF_APP_NETWORK_INTERFACE == CELLULAR_BG96 +#include "BG96Interface.h" +BG96Interface bg96; + +#else +#error "No connectivity method chosen. Please add 'config.network-interfaces.value' to your mbed_app.json (see README.md for more information)." +#endif // MBED_CONF_APP_NETWORK_INTERFACE + +/* \brief print_MAC - print_MAC - helper function to print out MAC address + * in: network_interface - pointer to network i/f + * bool log-messages print out logs or not + * MAC address is printed, if it can be acquired & log_messages is true. + * + */ +void print_MAC(NetworkInterface* network_interface, bool log_messages) { + const char *mac_addr = network_interface->get_mac_address(); + if( !log_messages ) + return; + if (mac_addr == NULL) + printf("[EasyConnect] ERROR - No MAC address\n"); + else + printf("[EasyConnect] MAC address %s\n", mac_addr); +} + + +/* \brief easy_connect easy_connect() function to connect the pre-defined network bearer, + * config done via mbed_app.json (see README.md for details). + * + * IN: bool log_messages print out diagnostics or not. + */ +NetworkInterface* easy_connect(bool log_messages) { + NetworkInterface* network_interface = NULL; + int connect_success = -1; + +#if MBED_CONF_APP_NETWORK_INTERFACE == ETHERNET + if (log_messages) { + printf("[EasyConnect] Using Ethernet\n"); + } + network_interface = ð + connect_success = eth.connect(); + +#elif MBED_CONF_APP_NETWORK_INTERFACE == CELLULAR_WNC14A2A + if (log_messages) { + printf("[EasyConnect] Using WNC14A2A\n"); + } + #if MBED_CONF_APP_WNC_DEBUG == true + printf("[EasyConnect] With WNC14A2A debug output set to 0x%02X\n",MBED_CONF_APP_WNC_DEBUG_SETTING); + wnc.doDebug(MBED_CONF_APP_WNC_DEBUG_SETTING); + #endif + network_interface = &wnc; + connect_success = wnc.connect(); + +#elif MBED_CONF_APP_NETWORK_INTERFACE == CELLULAR_BG96 + if (log_messages) { + printf("[EasyConnect] Using BG96\n"); + } + #if MBED_CONF_APP_BG96_DEBUG == true + printf("[EasyConnect] With BG96 debug output set to 0x%02X\n",MBED_CONF_APP_BG96_DEBUG_SETTING); + bg96.doDebug(MBED_CONF_APP_BG96_DEBUG_SETTING); + #endif + network_interface = &bg96; + connect_success = bg96.connect(); +#endif + + if(connect_success == 0) { + if (log_messages) { + printf("[EasyConnect] Connected to Network successfully\n"); + print_MAC(network_interface, log_messages); + } + } else { + if (log_messages) { + print_MAC(network_interface, log_messages); + printf("[EasyConnect] Connection to Network Failed %d!\n", connect_success); + } + return NULL; + } + + const char *ip_addr = network_interface->get_ip_address(); + if (ip_addr == NULL) { + if (log_messages) { + printf("[EasyConnect] ERROR - No IP address\n"); + } + return NULL; + } + + if (log_messages) { + printf("[EasyConnect] IP address %s\n", ip_addr); + } + return network_interface; +} + +/* \brief easy_get_netif - easy_connect function to get pointer to network interface + * without connecting to it. + * + * IN: bool log_messages print out diagnostics or not. + */ +NetworkInterface* easy_get_netif(bool log_messages) { +#if MBED_CONF_APP_NETWORK_INTERFACE == ETHERNET + if (log_messages) { + printf("[EasyConnect] Ethernet\n"); + } + return ð + +#elif MBED_CONF_APP_NETWORK_INTERFACE == CELLULAR_WNC14A2A + if (log_messages) { + printf("[EasyConnect] WNC14A2A\n"); + } + return &wnc; +#elif MBED_CONF_APP_NETWORK_INTERFACE == CELLULAR_BG96 + if (log_messages) { + printf("[EasyConnect] BG96\n"); + } + return &bg96; +#endif +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/easy-connect.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,53 @@ + +#ifndef __EASY_CONNECT_H__ +#define __EASY_CONNECT_H__ + +#include "mbed.h" + +#define CELLULAR_WNC14A2A 203 +#define CELLULAR_BG96 204 + +// This is address to mbed Device Connector +#define MBED_SERVER_ADDRESS "coap://api.connector.mbed.com:5684" + +/* \brief print_MAC - print_MAC - helper function to print out MAC address + * in: network_interface - pointer to network i/f + * bool log-messages print out logs or not + * MAC address is print, if it can be acquired & log_messages is true. + * + */ +void print_MAC(NetworkInterface* network_interface, bool log_messages); + + +/* \brief easy_connect - easy_connect function to connect the pre-defined network bearer, + * config done via mbed_app.json (see README.md for details). + * IN: bool log_messages print out diagnostics or not. + */ +NetworkInterface* easy_connect(bool log_messages); + +// This is address to mbed Device Connector +#define MBED_SERVER_ADDRESS "coap://api.connector.mbed.com:5684" + +/* \brief print_MAC - print_MAC - helper function to print out MAC address + * in: network_interface - pointer to network i/f + * bool log-messages print out logs or not + * MAC address is print, if it can be acquired & log_messages is true. + * + */ +void print_MAC(NetworkInterface* network_interface, bool log_messages); + + +/* \brief easy_connect - easy_connect function to connect the pre-defined network bearer, + * config done via mbed_app.json (see README.md for details). + * IN: bool log_messages print out diagnostics or not. + */ +NetworkInterface* easy_connect(bool log_messages = false); + +/* \brief easy_get_netif - easy_connect function to get pointer to network interface w/o connect it. + You might need this for example getting the WiFi interface, then doing a scan + and then connecting to one of the SSIDs found with a password end user supplies. + * IN: bool log_messages print out diagnostics or not. + */ +NetworkInterface* easy_get_netif(bool log_messages); + +#endif // __EASY_CONNECT_H__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/mbed_lib.json Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,4 @@ +{ + "name": "easy-connect", + "macros": ["DEFAULT_APN=\"m2m.com.attz\""] +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver.lib Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +https://github.com/Avnet/wnc14a2a-driver/#90928b81747ef4b0fb4fdd94705142175e014b30
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +ref: refs/heads/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/ORIG_HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +90928b81747ef4b0fb4fdd94705142175e014b30
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/config Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,11 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true +[remote "origin"] + fetch = +refs/heads/*:refs/remotes/origin/* + url = https://github.com/Avnet/wnc14a2a-driver/ +[branch "master"] + remote = origin + merge = refs/heads/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/description Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +Unnamed repository; edit this file 'description' to name the repository.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/hooks/applypatch-msg.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,15 @@ +#!/bin/sh +# +# An example hook script to check the commit log message taken by +# applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the commit message file. +# +# To enable this hook, rename this file to "applypatch-msg". + +. git-sh-setup +test -x "$GIT_DIR/hooks/commit-msg" && + exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} +:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/hooks/commit-msg.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,24 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/hooks/post-update.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,8 @@ +#!/bin/sh +# +# An example hook script to prepare a packed repository for use over +# dumb transports. +# +# To enable this hook, rename this file to "post-update". + +exec git update-server-info
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/hooks/pre-applypatch.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,14 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +. git-sh-setup +test -x "$GIT_DIR/hooks/pre-commit" && + exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"} +:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/hooks/pre-commit.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,50 @@ +#!/bin/sh +# +# An example hook script to verify what is about to be committed. +# Called by "git commit" with no arguments. The hook should +# exit with non-zero status after issuing an appropriate message if +# it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-commit". + +if git rev-parse --verify HEAD >/dev/null 2>&1 +then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +# If you want to allow non-ascii filenames set this variable to true. +allownonascii=$(git config hooks.allownonascii) + +# Redirect output to stderr. +exec 1>&2 + +# Cross platform projects tend to avoid non-ascii filenames; prevent +# them from being added to the repository. We exploit the fact that the +# printable range starts at the space character and ends with tilde. +if [ "$allownonascii" != "true" ] && + # Note that the use of brackets around a tr range is ok here, (it's + # even required, for portability to Solaris 10's /usr/bin/tr), since + # the square bracket bytes happen to fall in the designated range. + test $(git diff --cached --name-only --diff-filter=A -z $against | + LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 +then + echo "Error: Attempt to add a non-ascii file name." + echo + echo "This can cause problems if you want to work" + echo "with people on other platforms." + echo + echo "To be portable it is advisable to rename the file ..." + echo + echo "If you know what you are doing you can disable this" + echo "check using:" + echo + echo " git config hooks.allownonascii true" + echo + exit 1 +fi + +# If there are whitespace errors, print the offending file names and fail. +exec git diff-index --check --cached $against --
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/hooks/pre-rebase.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,169 @@ +#!/bin/sh +# +# Copyright (c) 2006, 2008 Junio C Hamano +# +# The "pre-rebase" hook is run just before "git rebase" starts doing +# its job, and can prevent the command from running by exiting with +# non-zero status. +# +# The hook is called with the following parameters: +# +# $1 -- the upstream the series was forked from. +# $2 -- the branch being rebased (or empty when rebasing the current branch). +# +# This sample shows how to prevent topic branches that are already +# merged to 'next' branch from getting rebased, because allowing it +# would result in rebasing already published history. + +publish=next +basebranch="$1" +if test "$#" = 2 +then + topic="refs/heads/$2" +else + topic=`git symbolic-ref HEAD` || + exit 0 ;# we do not interrupt rebasing detached HEAD +fi + +case "$topic" in +refs/heads/??/*) + ;; +*) + exit 0 ;# we do not interrupt others. + ;; +esac + +# Now we are dealing with a topic branch being rebased +# on top of master. Is it OK to rebase it? + +# Does the topic really exist? +git show-ref -q "$topic" || { + echo >&2 "No such branch $topic" + exit 1 +} + +# Is topic fully merged to master? +not_in_master=`git rev-list --pretty=oneline ^master "$topic"` +if test -z "$not_in_master" +then + echo >&2 "$topic is fully merged to master; better remove it." + exit 1 ;# we could allow it, but there is no point. +fi + +# Is topic ever merged to next? If so you should not be rebasing it. +only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` +only_next_2=`git rev-list ^master ${publish} | sort` +if test "$only_next_1" = "$only_next_2" +then + not_in_topic=`git rev-list "^$topic" master` + if test -z "$not_in_topic" + then + echo >&2 "$topic is already up-to-date with master" + exit 1 ;# we could allow it, but there is no point. + else + exit 0 + fi +else + not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` + /usr/bin/perl -e ' + my $topic = $ARGV[0]; + my $msg = "* $topic has commits already merged to public branch:\n"; + my (%not_in_next) = map { + /^([0-9a-f]+) /; + ($1 => 1); + } split(/\n/, $ARGV[1]); + for my $elem (map { + /^([0-9a-f]+) (.*)$/; + [$1 => $2]; + } split(/\n/, $ARGV[2])) { + if (!exists $not_in_next{$elem->[0]}) { + if ($msg) { + print STDERR $msg; + undef $msg; + } + print STDERR " $elem->[1]\n"; + } + } + ' "$topic" "$not_in_next" "$not_in_master" + exit 1 +fi + +<<\DOC_END + +This sample hook safeguards topic branches that have been +published from being rewound. + +The workflow assumed here is: + + * Once a topic branch forks from "master", "master" is never + merged into it again (either directly or indirectly). + + * Once a topic branch is fully cooked and merged into "master", + it is deleted. If you need to build on top of it to correct + earlier mistakes, a new topic branch is created by forking at + the tip of the "master". This is not strictly necessary, but + it makes it easier to keep your history simple. + + * Whenever you need to test or publish your changes to topic + branches, merge them into "next" branch. + +The script, being an example, hardcodes the publish branch name +to be "next", but it is trivial to make it configurable via +$GIT_DIR/config mechanism. + +With this workflow, you would want to know: + +(1) ... if a topic branch has ever been merged to "next". Young + topic branches can have stupid mistakes you would rather + clean up before publishing, and things that have not been + merged into other branches can be easily rebased without + affecting other people. But once it is published, you would + not want to rewind it. + +(2) ... if a topic branch has been fully merged to "master". + Then you can delete it. More importantly, you should not + build on top of it -- other people may already want to + change things related to the topic as patches against your + "master", so if you need further changes, it is better to + fork the topic (perhaps with the same name) afresh from the + tip of "master". + +Let's look at this example: + + o---o---o---o---o---o---o---o---o---o "next" + / / / / + / a---a---b A / / + / / / / + / / c---c---c---c B / + / / / \ / + / / / b---b C \ / + / / / / \ / + ---o---o---o---o---o---o---o---o---o---o---o "master" + + +A, B and C are topic branches. + + * A has one fix since it was merged up to "next". + + * B has finished. It has been fully merged up to "master" and "next", + and is ready to be deleted. + + * C has not merged to "next" at all. + +We would want to allow C to be rebased, refuse A, and encourage +B to be deleted. + +To compute (1): + + git rev-list ^master ^topic next + git rev-list ^master next + + if these match, topic has not merged in next at all. + +To compute (2): + + git rev-list master..topic + + if this is empty, it is fully merged to "master". + +DOC_END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/hooks/prepare-commit-msg.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to prepare the commit log message. +# Called by "git commit" with the name of the file that has the +# commit message, followed by the description of the commit +# message's source. The hook's purpose is to edit the commit +# message file. If the hook fails with a non-zero status, +# the commit is aborted. +# +# To enable this hook, rename this file to "prepare-commit-msg". + +# This hook includes three examples. The first comments out the +# "Conflicts:" part of a merge commit. +# +# The second includes the output of "git diff --name-status -r" +# into the message, just before the "git status" output. It is +# commented because it doesn't cope with --amend or with squashed +# commits. +# +# The third example adds a Signed-off-by line to the message, that can +# still be edited. This is rarely a good idea. + +case "$2,$3" in + merge,) + /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; + +# ,|template,) +# /usr/bin/perl -i.bak -pe ' +# print "\n" . `git diff --cached --name-status -r` +# if /^#/ && $first++ == 0' "$1" ;; + + *) ;; +esac + +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/hooks/update.sample Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,128 @@ +#!/bin/sh +# +# An example hook script to blocks unannotated tags from entering. +# Called by "git receive-pack" with arguments: refname sha1-old sha1-new +# +# To enable this hook, rename this file to "update". +# +# Config +# ------ +# hooks.allowunannotated +# This boolean sets whether unannotated tags will be allowed into the +# repository. By default they won't be. +# hooks.allowdeletetag +# This boolean sets whether deleting tags will be allowed in the +# repository. By default they won't be. +# hooks.allowmodifytag +# This boolean sets whether a tag may be modified after creation. By default +# it won't be. +# hooks.allowdeletebranch +# This boolean sets whether deleting branches will be allowed in the +# repository. By default they won't be. +# hooks.denycreatebranch +# This boolean sets whether remotely creating branches will be denied +# in the repository. By default this is allowed. +# + +# --- Command line +refname="$1" +oldrev="$2" +newrev="$3" + +# --- Safety check +if [ -z "$GIT_DIR" ]; then + echo "Don't run this script from the command line." >&2 + echo " (if you want, you could supply GIT_DIR then run" >&2 + echo " $0 <ref> <oldrev> <newrev>)" >&2 + exit 1 +fi + +if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then + echo "Usage: $0 <ref> <oldrev> <newrev>" >&2 + exit 1 +fi + +# --- Config +allowunannotated=$(git config --bool hooks.allowunannotated) +allowdeletebranch=$(git config --bool hooks.allowdeletebranch) +denycreatebranch=$(git config --bool hooks.denycreatebranch) +allowdeletetag=$(git config --bool hooks.allowdeletetag) +allowmodifytag=$(git config --bool hooks.allowmodifytag) + +# check for no description +projectdesc=$(sed -e '1q' "$GIT_DIR/description") +case "$projectdesc" in +"Unnamed repository"* | "") + echo "*** Project description file hasn't been set" >&2 + exit 1 + ;; +esac + +# --- Check types +# if $newrev is 0000...0000, it's a commit to delete a ref. +zero="0000000000000000000000000000000000000000" +if [ "$newrev" = "$zero" ]; then + newrev_type=delete +else + newrev_type=$(git cat-file -t $newrev) +fi + +case "$refname","$newrev_type" in + refs/tags/*,commit) + # un-annotated tag + short_refname=${refname##refs/tags/} + if [ "$allowunannotated" != "true" ]; then + echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 + echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 + exit 1 + fi + ;; + refs/tags/*,delete) + # delete tag + if [ "$allowdeletetag" != "true" ]; then + echo "*** Deleting a tag is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/tags/*,tag) + # annotated tag + if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 + then + echo "*** Tag '$refname' already exists." >&2 + echo "*** Modifying a tag is not allowed in this repository." >&2 + exit 1 + fi + ;; + refs/heads/*,commit) + # branch + if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then + echo "*** Creating a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/heads/*,delete) + # delete branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + refs/remotes/*,commit) + # tracking branch + ;; + refs/remotes/*,delete) + # delete tracking branch + if [ "$allowdeletebranch" != "true" ]; then + echo "*** Deleting a tracking branch is not allowed in this repository" >&2 + exit 1 + fi + ;; + *) + # Anything else (is there anything else?) + echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 + exit 1 + ;; +esac + +# --- Finished +exit 0
Binary file platform/ezconnect/wnc14a2a-driver/.git/index has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/info/exclude Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,25 @@ +.hg +.git +.svn +.CVS +.cvs +*.orig +.build +.export +.msub +.meta +.ctags* +*.uvproj +*.uvopt +*.project +*.cproject +*.launch +*.ewp +*.eww +Makefile +Debug +*.htm +*.settings +mbed_settings.py +*.py[cod] +# subrepo ignores \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/logs/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,3 @@ +0000000000000000000000000000000000000000 90928b81747ef4b0fb4fdd94705142175e014b30 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694024 +0000 clone: from https://github.com/Avnet/wnc14a2a-driver/ +90928b81747ef4b0fb4fdd94705142175e014b30 90928b81747ef4b0fb4fdd94705142175e014b30 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694025 +0000 checkout: moving from master to 90928b81747ef4b0fb4fdd94705142175e014b30 +90928b81747ef4b0fb4fdd94705142175e014b30 90928b81747ef4b0fb4fdd94705142175e014b30 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694025 +0000 checkout: moving from 90928b81747ef4b0fb4fdd94705142175e014b30 to master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/logs/refs/heads/master Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +0000000000000000000000000000000000000000 90928b81747ef4b0fb4fdd94705142175e014b30 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694024 +0000 clone: from https://github.com/Avnet/wnc14a2a-driver/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/logs/refs/remotes/origin/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +0000000000000000000000000000000000000000 90928b81747ef4b0fb4fdd94705142175e014b30 www-data <www-data@developer-sjc-cyan-compiler.local.mbed.org> 1527694024 +0000 clone: from https://github.com/Avnet/wnc14a2a-driver/
Binary file platform/ezconnect/wnc14a2a-driver/.git/objects/pack/pack-d1da25a6125e922b0dd6edf286190273a308427d.idx has changed
Binary file platform/ezconnect/wnc14a2a-driver/.git/objects/pack/pack-d1da25a6125e922b0dd6edf286190273a308427d.pack has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/packed-refs Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,2 @@ +# pack-refs with: peeled +90928b81747ef4b0fb4fdd94705142175e014b30 refs/remotes/origin/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/refs/heads/master Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +90928b81747ef4b0fb4fdd94705142175e014b30
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/.git/refs/remotes/origin/HEAD Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,1 @@ +ref: refs/remotes/origin/master
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/LICENSE Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/README.md Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,57 @@ + +# Easy Connect with the wnc14a2a-library + +## Specifying the connectivity method + +Add the following to your `mbed_app.json` file: + +```json +{ + "config": { + "network-interface":{ + "help": "options are ETHERNET, WIFI_ESP8266, WIFI_IDW0XX1, WIFI_ODIN, WIFI_RTW, WIFI_WIZFI310, WIFI_ISM43362, MESH_LOWPAN_ND, MESH_THREAD, CELLULAR_ONBOARD, CELLULAR_WNC14A2A", + "value": "CELLULAR_WNC14A2A" + } + }, + } +} +``` + +## Debug settings +You may also want to specify the following to obtain debug output. This is added to the "config" section: + +```json + "config": { + "wnc_debug": { + "help" : "enable or disable WNC debug messages.", + "value": 0 + }, + "wnc_debug_setting": { + "help" : "bit value 1 and/or 2 enable WncController debug output, bit value 4 enables mbed driver debug output.", + "value": "4" + } + } +``` + +where the wnc_debug is either 'true' or 'false' to enable or disable debug output and the `wnc_debug_setting` variable is a +bit-field with the following settings: + 0x01 - Basic WNC driver debug output + 0x02 - Comprehensive WNC driver debug output + 0x04 - Driver Enter/Exit debug information + 0x08 - Driver Event Queue debug information + 0x10 - SMS debug information + 0x20 - Dump Driver Arrays + +## Overriding settings +You need to increase the size of the Tx and Rx buffers used when communicating to the WNC device by adding (increas the buffer +size to 4KB from 256 Bytes): + + +```json + "target_overrides": { + "*": { + "drivers.uart-serial-txbuf-size": 4096, + "drivers.uart-serial-rxbuf-size": 4096 + } +``` +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/WNC14A2AInterface/WNC14A2AInterface.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,957 @@ +/** +* copyright (c) 2017-2018, James Flynn +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** +* @file WNC14A2AInterface.cpp +* @brief WNC14A2A implementation of NetworkInterfaceAPI for Mbed OS +* +* @author James Flynn +* +* @date 1-Feb-2018 +*/ + +#include "WNC14A2AInterface.h" +#include <Thread.h> +#include "mbed_events.h" +#include "WNCIO.h" + +#include <string> +#include <ctype.h> + +#define WNC_DEBUG 0 //1=enable the WNC startup debug output + //0=disable the WNC startup debug output +#define STOP_ON_FE 1 //1=hang forever if a fatal error occurs + //0=simply return failed response for all socket calls +#define DISPLAY_FE 1 //1 to display the fatal error when it occurs + //0 to NOT display the fatal error +#define RESETON_FE 0 //1 to cause the MCU to reset on fatal error + //0 to NOT reset the MCU + +/** Error Handling macros & data +* @brief The macros CHK_WNCFE is used to check if a fatal error has occured. If it has +* then execute the action specified: fail, void, null, resume +* +* CHK_WNCFE( condition-to-check, fail|void|null|resume ) +* +* 'fail' if you want FATAL_WNC_ERROR to be called. +* 'void' if you want to execute a void return +* 'null' if you want to execute a null return +* 'resume' if you simply want to resume program execution +* +* There are several settings that control how FATAL_WNC_ERROR behaves: +* 1) RESETON_FE determines if the system will reset or hang. +* 2) DISPLAY_FE determine if an error message is generated or not +* +* The DISPLAY_FE setting determines if a failure message is displayed. +* If set to 1, user sees this messageo: +* +* WNC FAILED @ source-file-name:source-file-line-number +* +* if not set, nothing is displayed. +*/ + +#define FATAL_FLAG WncController::WNC_NO_RESPONSE +#define WNC_GOOD WncController::WNC_ON + +#define RETfail return -1 +#define RETvoid return +#define RETnull return NULL +#define RETresume + +#define DORET(x) RET##x + +#define TOSTR(x) #x +#define INTSTR(x) TOSTR(x) +#define FATAL_STR (char*)(__FILE__ ":" INTSTR(__LINE__)) + +#if RESETON_FE == 1 //reset on fatal error +#define MCURESET ((*((volatile unsigned long *)0xE000ED0CU))=(unsigned long)((0x5fa<<16) | 0x04L)) +#define RSTMSG "RESET MCU! " +#else +#define MCURESET +#define RSTMSG "" +#endif + +#if DISPLAY_FE == 1 //display fatal error message +#define PFE {if(_debugUart)_debugUart->printf((char*)RSTMSG "\r\n>>WNC FAILED @ %s\r\n", FATAL_STR);} +#else +#define PFE +#endif + +#if STOP_ON_FE == 1 //halt cpu on fatal error +#define FATAL_WNC_ERROR(v) {_fatal_err_loc=FATAL_STR;PFE;MCURESET;while(1);} +#else +#define FATAL_WNC_ERROR(v) {_fatal_err_loc=FATAL_STR;PFE;DORET(v);} +#endif + +#define CHK_WNCFE(x,y) if( x ){FATAL_WNC_ERROR(y);} + +// +// Define different levels of debug output +// +#define DBGMSG_DRV 0x04 //driver enter/exit info +#define DBGMSG_EQ 0x08 //driver event queue info +#define DBGMSG_SMS 0x10 //driver SMS info +#define DBGMSG_ARRY 0x20 //dump driver arrays + +#define WNC14A2A_READ_TIMEOUTMS 4000 //duration to read no data to receive in MS +#define WNC14A2A_COMMUNICATION_TIMEOUT 100 //how long (ms) to wait for a WNC14A2A connect response +#define WNC_BUFF_SIZE 1500 //total number of bytes in a single WNC call +#define UART_BUFF_SIZE 4096 //size of our internal uart buffer.. define in *.json file + +#define EQ_FREQ 250 //frequency in ms to check for Tx/Rx data +#define EQ_FREQ_SLOW 2000 //frequency in ms to check when in slow monitor mode + +// +// The WNC device does not generate interrutps on received data, so this software polls +// for data availablility. To implement a non-blocking mode, simulate interrupts using +// mbed OS Event Queues. These Constants are used to manage the Rx/Tx states. +// +#define READ_INIT 10 +#define READ_START 11 +#define READ_ACTIVE 12 +#define DATA_AVAILABLE 13 +#define TX_IDLE 20 +#define TX_STARTING 21 +#define TX_ACTIVE 22 +#define TX_COMPLETE 23 + +#if MBED_CONF_APP_WNC_DEBUG == true +#define debugOutput(...) WNC14A2AInterface::_dbOut(__VA_ARGS__) +#define debugDump_arry(...) WNC14A2AInterface::_dbDump_arry(__VA_ARGS__) +#else +#define debugOutput(...) {/* __VA_ARGS__ */} +#define debugDump_arry(...) {/* __VA_ARGS__ */} +#endif + +/* Constructor +* +* @brief May be invoked with or without the debug pointer. +* @note After the constructor has completed, call check +* m_errors to determine if any errors occured. Possible values: +* NSAPI_ERROR_UNSUPPORTED +* NSAPI_ERROR_DEVICE_ERROR +*/ +WNC14A2AInterface::WNC14A2AInterface(WNCDebug *dbg) : + m_wncpoweredup(0), + m_debug(0), + m_pwnc(NULL), + m_errors(NSAPI_ERROR_OK), + m_smsmoning(0), + _active_socket(0), + mdmUart(MBED_CONF_WNC14A2A_LIBRARY_WNC_TXD,MBED_CONF_WNC14A2A_LIBRARY_WNC_RXD,115200), + wnc_io(&mdmUart) +{ + _debugUart = dbg; + memset(_mac_address,0x00,sizeof(_mac_address)); + memset(_socTxS,0x00,sizeof(_socTxS)); + memset(_socRxS,0x00,sizeof(_socRxS)); + for( unsigned int i=0; i<WNC14A2A_SOCKET_COUNT; i++ ) { + _sockets[i].socket = i; + _sockets[i].addr = NULL; + _sockets[i].opened=false; + + _sockets[i].connected=false; + _sockets[i].proto=1; + _socRxS[i].m_rx_socket=i; + _socTxS[i].m_tx_socket=i; + } +} + +//! Standard destructor +WNC14A2AInterface::~WNC14A2AInterface() +{ + if( m_pwnc ) + delete m_pwnc; //free the existing WncControllerK64F object +} + +// - - - - - - - +// SMS Functions +// - - - - - - - + +char* WNC14A2AInterface::getSMSnbr( void ) +{ + char * ret=NULL; + string iccid_str; + static string msisdn_str; + + if( !m_pwnc ) { + m_errors=NSAPI_ERROR_DEVICE_ERROR; + return NULL; + } + CHK_WNCFE(( m_pwnc->getWncStatus() == FATAL_FLAG ), null); + + _pwnc_mutex.lock(); + if( !m_pwnc->getICCID(&iccid_str) ) { + _pwnc_mutex.unlock(); + return ret; + } + + if( m_pwnc->convertICCIDtoMSISDN(iccid_str, &msisdn_str) ) + ret = (char*)msisdn_str.c_str(); + _pwnc_mutex.unlock(); + return ret; +} + +void WNC14A2AInterface::sms_attach(void (*callback)(IOTSMS *)) +{ + debugOutput("ENTER/EXIT sms_attach()"); + _sms_cb = callback; +} + +void WNC14A2AInterface::sms_start(void) +{ + _pwnc_mutex.lock(); + m_pwnc->deleteSMSTextFromMem('*'); + _pwnc_mutex.unlock(); +} + +void WNC14A2AInterface::sms_listen(uint16_t pp) +{ + debugOutput("ENTER sms_listen(%d)",pp); + + if( m_smsmoning ) + m_smsmoning = false; + if( pp < 1) + pp = 30; + + debugOutput("setup sms_listen event queue"); + _smsThread.start(callback(&sms_queue,&EventQueue::dispatch_forever)); + + sms_start(); + sms_queue.call_every(pp*1000, mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::handle_sms_event)); + + m_smsmoning = true; + debugOutput("EXIT sms_listen()"); +} + +void WNC14A2AInterface::handle_sms_event() +{ + int msgs_available; + debugOutput("ENTER handle_sms_event()"); + + if ( _sms_cb && m_smsmoning ) { + _pwnc_mutex.lock(); + msgs_available = m_pwnc->readUnreadSMSText(&m_smsmsgs, true); + _pwnc_mutex.unlock(); + if( msgs_available ) { + debugOutput("Have %d unread texts present",m_smsmsgs.msgCount); + for( int i=0; i< m_smsmsgs.msgCount; i++ ) { + m_MsgText.number = m_smsmsgs.e[i].number; + m_MsgText.date = m_smsmsgs.e[i].date; + m_MsgText.time = m_smsmsgs.e[i].time; + m_MsgText.msg = m_smsmsgs.e[i].msg; + _sms_cb(&m_MsgText); + } + } + } + debugOutput("EXIT handle_sms_event"); +} + +int WNC14A2AInterface::getSMS(IOTSMS **pmsg) +{ + int msgs_available=0; + + debugOutput("ENTER getSMS()"); + if( !m_pwnc ) + m_errors=NSAPI_ERROR_DEVICE_ERROR; + else{ + CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail); + _pwnc_mutex.lock(); + msgs_available = m_pwnc->readUnreadSMSText(&m_smsmsgs, true); + _pwnc_mutex.unlock(); + } + + if( msgs_available ) { + debugOutput("Have %d unread texts present",m_smsmsgs.msgCount); + for( int i=0; i< m_smsmsgs.msgCount; i++ ) { + m_MsgText_array[i].number = m_smsmsgs.e[i].number; + m_MsgText_array[i].date = m_smsmsgs.e[i].date; + m_MsgText_array[i].time = m_smsmsgs.e[i].time; + m_MsgText_array[i].msg = m_smsmsgs.e[i].msg; + pmsg[i] = (IOTSMS*)&m_MsgText_array[i]; + } + msgs_available = m_smsmsgs.msgCount; + } + debugOutput("EXIT getSMS"); + return msgs_available; +} + + +int WNC14A2AInterface::sendIOTSms(const string& number, const string& message) +{ + debugOutput("ENTER sendIOTSms(%s,%s)",number.c_str(), message.c_str()); + + if( !m_pwnc ) + return (m_errors=NSAPI_ERROR_DEVICE_ERROR); + CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail); + + _pwnc_mutex.lock(); + int i = m_pwnc->sendSMSText((char*)number.c_str(), message.c_str()); + _pwnc_mutex.unlock(); + + debugOutput("EXIT sendIOTSms(%s,%s)",number.c_str(), message.c_str()); + return i; +} + + +// - - - - - - - - - - - +// WNC Control Functions +// - - - - - - - - - - - + +nsapi_error_t WNC14A2AInterface::connect() //can be called with no arguments or with arguments +{ + debugOutput("ENTER connect(void)"); + return connect(NULL,NULL,NULL); +} + +nsapi_error_t WNC14A2AInterface::connect(const char *apn, const char *username, const char *password) +{ + // + // GPIO Pins used to initialize the WNC parts on the Avnet WNC Shield + // + // on powerup, 0 = boot mode, 1 = normal boot + // 0=let modem sleep, 1=keep modem awake -- Note: pulled high on shield + // active high + // 0 = disabled (all signals high impedence, 1 = translation active + // WNC doesn't utilize RTS/CTS but the pin is connected + + static DigitalOut mdm_uart2_rx_boot_mode_sel(MBED_CONF_WNC14A2A_LIBRARY_WNC_RX_BOOT_SEL); + static DigitalOut mdm_power_on(MBED_CONF_WNC14A2A_LIBRARY_WNC_POWER_ON); + static DigitalOut mdm_wakeup_in(MBED_CONF_WNC14A2A_LIBRARY_WNC_WAKEUP); + static DigitalOut mdm_reset(MBED_CONF_WNC14A2A_LIBRARY_WNC_RESET); + static DigitalOut shield_3v3_1v8_sig_trans_ena(MBED_CONF_WNC14A2A_LIBRARY_WNC_LVLTRANSLATOR); + static DigitalOut mdm_uart1_cts(MBED_CONF_WNC14A2A_LIBRARY_WNC_CTS); + + //! associations for the controller class to use. Order of pins is critical. + static WncControllerK64F_fk::WncGpioPinListK64F wncPinList = { + &mdm_uart2_rx_boot_mode_sel, + &mdm_power_on, + &mdm_wakeup_in, + &mdm_reset, + &shield_3v3_1v8_sig_trans_ena, + &mdm_uart1_cts + }; + + debugOutput("ENTER connect(apn,user,pass)"); + + if( m_pwnc == NULL ) { + m_pwnc = new WncControllerK64F_fk::WncControllerK64F(&wncPinList, &wnc_io, _debugUart); + if( !m_pwnc ) { + debugOutput("FAILED to open WncControllerK64!"); + m_errors = NSAPI_ERROR_DEVICE_ERROR; + return NSAPI_ERROR_NO_MEMORY; + } + CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail); + #if MBED_CONF_APP_WNC_DEBUG == true + m_pwnc->enableDebug( (MBED_CONF_APP_WNC_DEBUG_SETTING&1), (MBED_CONF_APP_WNC_DEBUG_SETTING&2) ); + #endif + } + + _eqThread.start(callback(&wnc_queue,&EventQueue::dispatch_forever)); + + if (!apn) + apn = "m2m.com.attz"; + + _pwnc_mutex.lock(); + if (!m_wncpoweredup) { + debugOutput("call powerWncOn(%s,40)",apn); + m_wncpoweredup=m_pwnc->powerWncOn(apn,40); + m_errors = m_wncpoweredup? 1:0; + } + else { //powerWncOn already called, set a new APN + debugOutput("set APN=%s",apn); + m_errors = m_pwnc->setApnName(apn)? 1:0; + } + + m_errors |= m_pwnc->getWncNetworkingStats(&myNetStats)? 2:0; + _pwnc_mutex.unlock(); + + debugOutput("EXIT connect (%02X)",m_errors); + return (!m_errors)? NSAPI_ERROR_NO_CONNECTION : NSAPI_ERROR_OK; +} + +const char* WNC14A2AInterface::getWNCRev(void) +{ + if( m_pwnc ) { + const char * str = m_pwnc->getFirmRev(); + return &str[12]; + } + else + return NULL; +} + + +const char *WNC14A2AInterface::get_ip_address() +{ + const char *ptr=NULL; + + if( !m_pwnc ) { + m_errors=NSAPI_ERROR_DEVICE_ERROR; + return ptr; + } + CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), null); + + _pwnc_mutex.lock(); + if ( m_pwnc->getWncNetworkingStats(&myNetStats) ) { + _pwnc_mutex.unlock(); + ptr = &myNetStats.ip[0]; + } + else{ + _pwnc_mutex.unlock(); + m_errors=NSAPI_ERROR_NO_CONNECTION; + } + return ptr; +} + +const char *WNC14A2AInterface::get_mac_address() +{ + string mac, str; + debugOutput("ENTER get_mac_address()"); + + if( m_pwnc ) { + CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), null); + _pwnc_mutex.lock(); + if( m_pwnc->getICCID(&str) ) { + _pwnc_mutex.unlock(); + mac = str.substr(3,20); + mac[2]=mac[5]=mac[8]=mac[11]=mac[14]=':'; + strncpy(_mac_address, mac.c_str(), mac.length()); + debugOutput("EXIT get_mac_address() - %s",_mac_address); + return _mac_address; + } + _pwnc_mutex.unlock(); + } + debugOutput("EXIT get_mac_address() - NULL"); + return NULL; +} + +NetworkStack *WNC14A2AInterface::get_stack() { + debugOutput("ENTER/EXIT get_stack()"); + return this; +} + +nsapi_error_t WNC14A2AInterface::disconnect() +{ + debugOutput("ENTER/EXIT disconnect()"); + return NSAPI_ERROR_OK; +} + +nsapi_error_t WNC14A2AInterface::set_credentials(const char *apn, const char *username, const char *password) +{ + + m_errors=NSAPI_ERROR_OK; + debugOutput("ENTER set_credentials()"); + + if( !m_pwnc ) + return (m_errors=NSAPI_ERROR_DEVICE_ERROR); + CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail); + + if( !apn ) + return (m_errors=NSAPI_ERROR_PARAMETER); + + _pwnc_mutex.lock(); + if( !m_pwnc->setApnName(apn) ) + m_errors=NSAPI_ERROR_DEVICE_ERROR; + _pwnc_mutex.unlock(); + debugOutput("EXIT set_credentials()"); + return m_errors; +} + +bool WNC14A2AInterface::registered() +{ + debugOutput("ENTER registered()"); + m_errors=NSAPI_ERROR_OK; + + if( !m_pwnc ) { + return (m_errors=NSAPI_ERROR_DEVICE_ERROR); + } + CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail); + + _pwnc_mutex.lock(); + if ( m_pwnc->getWncStatus() != WNC_GOOD ) + m_errors=NSAPI_ERROR_NO_CONNECTION; + _pwnc_mutex.unlock(); + + debugOutput("EXIT registered()"); + return (m_errors==NSAPI_ERROR_OK); +} + + +void WNC14A2AInterface::doDebug( int v ) +{ + #if MBED_CONF_APP_WNC_DEBUG == true + m_debug= v; + debugOutput("SET debug flag to 0x%02X",v); + #endif +} + +/** function to dump a user provided array. +* +* @author James Flynn +* @param data pointer to the data array to dump +* @param size number of bytes to dump +* @return void +* @date 1-Feb-2018 +*/ +void WNC14A2AInterface::_dbDump_arry( const uint8_t* data, unsigned int size ) +{ + #if MBED_CONF_APP_WNC_DEBUG == true + char buffer[256]; + unsigned int i, k; + + if( _debugUart != NULL && (m_debug & DBGMSG_ARRY) ) { + for (i=0; i<size; i+=16) { + sprintf(buffer,"[WNC Driver]:0x%04X: ",i); + _debugUart->puts(buffer); + for (k=0; k<16; k++) { + sprintf(buffer, "%02X ", data[i+k]); + _debugUart->puts(buffer); + } + _debugUart->puts(" -- "); + for (k=0; k<16; k++) { + sprintf(buffer, "%2c", isprint(data[i+k])? data[i+k]:'.'); + _debugUart->puts(buffer); + } + _debugUart->puts("\n\r"); + } + } + #endif +} + +void WNC14A2AInterface::_dbOut(const char *format, ...) +{ + #if MBED_CONF_APP_WNC_DEBUG == true + char buffer[256]; + + sprintf(buffer,"[WNC Driver]: "); + if( _debugUart != NULL && (m_debug & (DBGMSG_DRV|DBGMSG_EQ|DBGMSG_SMS)) ) { + va_list args; + va_start (args, format); + _debugUart->puts(buffer); + if( m_debug & DBGMSG_DRV ) + vsnprintf(buffer, sizeof(buffer), format, args); + if( m_debug & DBGMSG_EQ ) + vsnprintf(buffer, sizeof(buffer), format, args); + if( m_debug & DBGMSG_SMS ) + vsnprintf(buffer, sizeof(buffer), format, args); + _debugUart->puts(buffer); + _debugUart->putc('\n'); + va_end (args); + } + #endif +} + +// - - - - - - - - - - - - - - - +// WNC Socket Based operatioins +// - - - - - - - - - - - - - - - + +nsapi_error_t WNC14A2AInterface::gethostbyname(const char* name, SocketAddress *address, nsapi_version_t version) +{ + nsapi_error_t ret = NSAPI_ERROR_OK; + char ipAddrStr[25]; + + debugOutput("ENTER gethostbyname(); IP=%s; PORT=%d; URL=%s;", address->get_ip_address(), address->get_port(), name); + + if( !m_pwnc ) + return (m_errors=NSAPI_ERROR_DEVICE_ERROR); + CHK_WNCFE((m_pwnc->getWncStatus()==FATAL_FLAG), fail); + + memset(ipAddrStr,0x00,sizeof(ipAddrStr)); + + //Execute DNS query. + _pwnc_mutex.lock(); + if( !m_pwnc->resolveUrl(_active_socket, name) ) + ret = m_errors = NSAPI_ERROR_DEVICE_ERROR; + + //Get IP address that the URL was resolved to + if( !m_pwnc->getIpAddr(_active_socket, ipAddrStr) ) + ret = m_errors = NSAPI_ERROR_DEVICE_ERROR; + _pwnc_mutex.unlock(); + + address->set_ip_address(ipAddrStr); + + debugOutput("EXIT gethostbyname(); IP=%s; PORT=%d; URL=%s;", address->get_ip_address(), address->get_port(), name); + return (m_errors = ret); +} + +int WNC14A2AInterface::socket_open(void **handle, nsapi_protocol_t proto) +{ + unsigned int i; + + debugOutput("ENTER socket_open()"); + + //find the next available socket... + for( i=0; i<WNC14A2A_SOCKET_COUNT; i++ ) + if( !_sockets[i].opened ) + break; + + if( i == WNC14A2A_SOCKET_COUNT ) { + m_errors=NSAPI_ERROR_NO_SOCKET; + return -1; + } + + _sockets[i].socket = i; //save index later + _sockets[i].opened = true; + _sockets[i].connected=false; + _sockets[i].proto = (proto==NSAPI_UDP)?0:1; + _sockets[i]._callback = NULL; + _sockets[i]._cb_data = NULL; + + _socRxS[i].m_rx_wnc_state = READ_START; + _socRxS[i].m_rx_disTO = false; + _socTxS[i].m_tx_wnc_state = TX_IDLE; + + *handle = &_sockets[i]; + + debugOutput("EXIT socket_open; Socket=%d, OPEN=%s, protocol =%s", + i, _sockets[i].opened?"YES":"NO", (!_sockets[i].proto)?"UDP":"TCP"); + + return (m_errors = NSAPI_ERROR_OK); +} + +int WNC14A2AInterface::socket_connect(void *handle, const SocketAddress &address) +{ + WNCSOCKET *wnc = (WNCSOCKET *)handle; + int err = 0; + + debugOutput("ENTER socket_connect(); Socket=%d; IP=%s; PORT=%d;", wnc->socket, address.get_ip_address(), address.get_port()); + + if (!wnc->opened ) + return (m_errors = NSAPI_ERROR_NO_SOCKET); + + wnc->addr = address; + + _pwnc_mutex.lock(); + if( wnc->url.empty() ) + err = !m_pwnc->openSocketIpAddr(wnc->socket, address.get_ip_address(), address.get_port(), wnc->proto, WNC14A2A_COMMUNICATION_TIMEOUT); + else + err = !m_pwnc->openSocketUrl(wnc->socket, wnc->url.c_str(), wnc->addr.get_port(), wnc->proto); + _pwnc_mutex.unlock(); + + if( !err ) { + wnc->connected = true; + _socRxS[wnc->socket].m_rx_wnc_state = READ_START; + _socTxS[wnc->socket].m_tx_wnc_state = TX_IDLE; + + if( wnc->_callback != NULL ) + wnc->_callback( wnc->_cb_data ); + } + + return err; +} + +int WNC14A2AInterface::socket_close(void *handle) +{ + WNCSOCKET *wnc = (WNCSOCKET*)handle; + RXEVENT *rxsock; + TXEVENT *txsock; + bool err = false; + + debugOutput("ENTER socket_close()"); + + rxsock = &_socRxS[wnc->socket]; + txsock = &_socTxS[wnc->socket]; + + txsock->m_tx_wnc_state = TX_IDLE; //reset TX state + if( rxsock->m_rx_wnc_state != READ_START ) { //reset RX state + rxsock->m_rx_disTO=false; + while( rxsock->m_rx_wnc_state != DATA_AVAILABLE ) + wait(1); //someone called close while a read was happening + } + + _pwnc_mutex.lock(); + if( !m_pwnc->closeSocket(wnc->socket) ) { + m_errors = NSAPI_ERROR_DEVICE_ERROR; + err = true; + } + _pwnc_mutex.unlock(); + + if( !err ) { + wnc->opened = false; //no longer in use + wnc->addr = NULL; //not open + wnc->connected= false; + wnc->proto = 1; //assume TCP for now + m_errors = NSAPI_ERROR_OK; + wnc->_cb_data = NULL; + wnc->_callback= NULL; + } + + debugOutput("EXIT socket_close()"); + return err; +} + + +void WNC14A2AInterface::socket_attach(void *handle, void (*callback)(void *), void *data) +{ + WNCSOCKET *wnc = (WNCSOCKET *)handle; + + debugOutput("ENTER/EXIT socket_attach()"); + wnc->_callback = callback; + wnc->_cb_data = data; +} + +int WNC14A2AInterface::socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size) +{ + WNCSOCKET *wnc = (WNCSOCKET *)handle; + + debugOutput("ENTER socket_sendto()"); + + if (!wnc->connected) { + int err = socket_connect(wnc, address); + if (err < 0) + return err; + } + wnc->addr = address; + + debugOutput("EXIT socket_sendto()"); + return socket_send(wnc, data, size); +} + +int WNC14A2AInterface::socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size) +{ + WNCSOCKET *wnc = (WNCSOCKET *)handle; + debugOutput("ENTER socket_recvfrom()"); + + if (!wnc->connected) { + debugOutput("need to open a WNC socket first"); + int err = socket_connect(wnc, *address); + if (err < 0) + return err; + } + + int ret = socket_recv(wnc, (char *)buffer, size); + if (ret >= 0 && address) + *address = wnc->addr; + debugOutput("EXIT socket_recvfrom()"); + return ret; +} + + +int inline WNC14A2AInterface::socket_accept(nsapi_socket_t server, nsapi_socket_t *handle, SocketAddress *address) +{ + debugOutput("ENTER/EXIT socket_accept() -- not supported"); + m_errors = NSAPI_ERROR_UNSUPPORTED; + return -1; +} + +int inline WNC14A2AInterface::socket_bind(void *handle, const SocketAddress &address) +{ + WNCSOCKET *wnc = (WNCSOCKET *)handle; + + debugOutput("ENTER/EXIT socket_bind(), use address '%s', port %d", address.get_ip_address(),address.get_port()); + _socRxS[wnc->socket].m_rx_disTO=true; //for us, simply disable the Rx timeout to keep monitoring for data + return (m_errors = NSAPI_ERROR_OK); +} + + +int inline WNC14A2AInterface::socket_listen(void *handle, int backlog) +{ + debugOutput("ENTER/EXIT socket_listen() -- not supported"); + m_errors = NSAPI_ERROR_UNSUPPORTED; + return -1; +} + + +int WNC14A2AInterface::socket_send(void *handle, const void *data, unsigned size) +{ + WNCSOCKET *wnc = (WNCSOCKET *)handle; + TXEVENT *txsock; + + debugOutput("ENTER socket_send() send %d bytes",size); + txsock = &_socTxS[wnc->socket]; + + if( size < 1 || data == NULL ) // should never happen but have seen it + return 0; + + switch( txsock->m_tx_wnc_state ) { + case TX_IDLE: + txsock->m_tx_wnc_state = TX_STARTING; + debugDump_arry((const uint8_t*)data,size); + txsock->m_tx_dptr = (uint8_t*)data; + txsock->m_tx_orig_size = size; + txsock->m_tx_req_size = (uint32_t)size; + txsock->m_tx_total_sent= 0; + txsock->m_tx_callback = wnc->_callback; + txsock->m_tx_cb_data = wnc->_cb_data; + + if( txsock->m_tx_req_size > UART_BUFF_SIZE ) + txsock->m_tx_req_size= UART_BUFF_SIZE; + + if( !tx_event(txsock) ) { //if we didn't sent all the data at once, continue in background + txsock->m_tx_wnc_state = TX_ACTIVE; + wnc_queue.call_in(EQ_FREQ,mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::wnc_eq_event)); + return NSAPI_ERROR_WOULD_BLOCK; + } + // fall through + + case TX_COMPLETE: + debugOutput("EXIT socket_send(), sent %d bytes", txsock->m_tx_total_sent); + txsock->m_tx_wnc_state = TX_IDLE; + return txsock->m_tx_total_sent; + + case TX_ACTIVE: + case TX_STARTING: + return NSAPI_ERROR_WOULD_BLOCK; + + default: + debugOutput("EXIT socket_send(), NSAPI_ERROR_DEVICE_ERROR"); + return (m_errors = NSAPI_ERROR_DEVICE_ERROR); + } +} + +int WNC14A2AInterface::tx_event(TXEVENT *ptr) +{ + debugOutput("ENTER tx_event(), socket %d",ptr->m_tx_socket); + + _pwnc_mutex.lock(); + if( m_pwnc->write(ptr->m_tx_socket, ptr->m_tx_dptr, ptr->m_tx_req_size) ) + ptr->m_tx_total_sent += ptr->m_tx_req_size; + _pwnc_mutex.unlock(); + + if( ptr->m_tx_total_sent < ptr->m_tx_orig_size ) { + ptr->m_tx_dptr += ptr->m_tx_req_size; + ptr->m_tx_req_size = ptr->m_tx_orig_size-ptr->m_tx_total_sent; + + if( ptr->m_tx_req_size > UART_BUFF_SIZE) + ptr->m_tx_req_size= UART_BUFF_SIZE; + + debugOutput("EXIT tx_event(), send %d more bytes.",ptr->m_tx_req_size); + return 0; + } + debugOutput("EXIT tx_event, socket %d, data sent",ptr->m_tx_socket); + ptr->m_tx_wnc_state = TX_COMPLETE; + if( ptr->m_tx_callback != NULL ) + ptr->m_tx_callback( ptr->m_tx_cb_data ); + ptr->m_tx_cb_data = NULL; + ptr->m_tx_callback = NULL; + + return 1; +} + +int WNC14A2AInterface::socket_recv(void *handle, void *data, unsigned size) +{ + WNCSOCKET *wnc = (WNCSOCKET *)handle; + RXEVENT *rxsock; + + rxsock = &_socRxS[wnc->socket]; + debugOutput("ENTER socket_recv(), socket %d, request %d bytes",wnc->socket, size); + + if( size < 1 || data == NULL ) { // should never happen + return 0; + } + + switch( rxsock->m_rx_wnc_state ) { + case READ_START: //need to start a read sequence of events + rxsock->m_rx_wnc_state= READ_INIT; + rxsock->m_rx_dptr = (uint8_t*)data; + rxsock->m_rx_req_size = (uint32_t)size; + rxsock->m_rx_total_cnt= 0; + rxsock->m_rx_timer = 0; + rxsock->m_rx_return_cnt=0; + + if( rxsock->m_rx_req_size > WNC_BUFF_SIZE) + rxsock->m_rx_req_size= WNC_BUFF_SIZE; + + rxsock->m_rx_callback = wnc->_callback; + rxsock->m_rx_cb_data = wnc->_cb_data; + + if( !rx_event(rxsock) ){ + rxsock->m_rx_wnc_state = READ_ACTIVE; + wnc_queue.call_in(EQ_FREQ,mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::wnc_eq_event)); + return NSAPI_ERROR_WOULD_BLOCK; + } + // fall through + + case DATA_AVAILABLE: + debugOutput("EXIT socket_recv(),socket %d, return %d bytes",wnc->socket, rxsock->m_rx_return_cnt); + debugDump_arry((const uint8_t*)data,rxsock->m_rx_return_cnt); + rxsock->m_rx_wnc_state = READ_START; + return rxsock->m_rx_return_cnt; + + case READ_ACTIVE: + case READ_INIT: + rxsock->m_rx_timer = 0; //reset the time-out timer + return NSAPI_ERROR_WOULD_BLOCK; + + default: + debugOutput("EXIT socket_recv(), NSAPI_ERROR_DEVICE_ERROR"); + return (m_errors = NSAPI_ERROR_DEVICE_ERROR); + } +} + + +int WNC14A2AInterface::rx_event(RXEVENT *ptr) +{ + debugOutput("ENTER rx_event() for socket %d", ptr->m_rx_socket); + _pwnc_mutex.lock(); + int cnt = m_pwnc->read(ptr->m_rx_socket, ptr->m_rx_dptr, ptr->m_rx_req_size); + _pwnc_mutex.unlock(); + + if( cnt ) { //got data, return it to the caller + debugOutput("data received on socket %d, cnt=%d", ptr->m_rx_socket,cnt); + ptr->m_rx_wnc_state = DATA_AVAILABLE; + ptr->m_rx_return_cnt = cnt; + if( ptr->m_rx_callback != NULL ) + ptr->m_rx_callback( ptr->m_rx_cb_data ); + ptr->m_rx_cb_data = NULL; + ptr->m_rx_callback = NULL; + return 1; + } + if( ++ptr->m_rx_timer > (WNC14A2A_READ_TIMEOUTMS/EQ_FREQ) && !ptr->m_rx_disTO ) { //timed out waiting, return 0 to caller + debugOutput("EXIT rx_event(), rx data TIME-OUT!"); + ptr->m_rx_wnc_state = DATA_AVAILABLE; + ptr->m_rx_return_cnt = 0; + if( ptr->m_rx_callback != NULL ) + ptr->m_rx_callback( ptr->m_rx_cb_data ); + ptr->m_rx_cb_data = NULL; + ptr->m_rx_callback = NULL; + return 1; + } + + debugOutput("EXIT rx_event(), socket %d, sechedule for more data.",ptr->m_rx_socket); + return 0; +} + +void WNC14A2AInterface::wnc_eq_event() +{ + int done = 1; + bool goSlow = true; + + for( unsigned int i=0; i<WNC14A2A_SOCKET_COUNT; i++ ) { + if( _socRxS[i].m_rx_wnc_state == READ_ACTIVE || _socRxS[i].m_rx_disTO) { + done &= rx_event(&_socRxS[i]); + goSlow &= ( _socRxS[i].m_rx_timer > ((WNC14A2A_READ_TIMEOUTMS/EQ_FREQ)*(EQ_FREQ_SLOW/EQ_FREQ)) ); + + if( goSlow ) + _socRxS[i].m_rx_timer = (WNC14A2A_READ_TIMEOUTMS/EQ_FREQ)*(EQ_FREQ_SLOW/EQ_FREQ); + } + + if( _socTxS[i].m_tx_wnc_state == TX_ACTIVE ) { + goSlow = false; + debugOutput("CALL TX_event() for socket %d", i); + done &= tx_event(&_socTxS[i]); + } + } + + if( !done ) + wnc_queue.call_in((goSlow?EQ_FREQ_SLOW:EQ_FREQ),mbed::Callback<void()>((WNC14A2AInterface*)this,&WNC14A2AInterface::wnc_eq_event)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/WNC14A2AInterface/WNC14A2AInterface.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,508 @@ +/** +* copyright (c) 2017-2018, James Flynn +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** +* @file WNC14A2AInterface.h +* @brief Implements a standard NetworkInterface class for use with WNC M14A2A +* data module. +* +* @author James Flynn +* +* @date 1-Feb-2018 +* +*/ + +#ifndef WNC14A2A_INTERFACE_H +#define WNC14A2A_INTERFACE_H + +#include <stdint.h> + +#include "mbed.h" +#include "Callback.h" +#include "WNCDebug.h" +#include "WncControllerK64F/WncControllerK64F.h" + +#define WNC14A2A_SOCKET_COUNT WncController::MAX_NUM_WNC_SOCKETS + +// If target board does not support Arduino pins, define pins as Not Connected +#if defined(TARGET_FF_ARDUINO) +#if !defined(MBED_CONF_WNC14A2A_LIBRARY_WNC_RX_BOOT_SEL) +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_RX_BOOT_SEL D1 +#endif +#if !defined(MBED_CONF_WNC14A2A_LIBRARY_WNC_POWER_ON) +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_POWER_ON D2 +#endif +#if !defined(MBED_CONF_WNC14A2A_LIBRARY_WNC_WAKEUP) +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_WAKEUP D6 +#endif +#if !defined(MBED_CONF_WNC14A2A_LIBRARY_WNC_RESET) +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_RESET D8 +#endif +#if !defined(MBED_CONF_WNC14A2A_LIBRARY_WNC_LVLTRANSLATOR) +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_LVLTRANSLATOR D9 +#endif +#if !defined(MBED_CONF_WNC14A2A_LIBRARY_WNC_CTS) +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_CTS D10 +#endif +#if !defined(MBED_CONF_WNC14A2A_LIBRARY_WNC_RXD) +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_RXD D11 +#endif +#if !defined(MBED_CONF_WNC14A2A_LIBRARY_WNC_TXD) +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_TXD D12 +#endif +#else // !defined(TARGET_FF_ARDUINO) +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_RX_BOOT_SEL NC +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_POWER_ON NC +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_WAKEUP NC +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_RESET NC +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_LVLTRANSLATOR NC +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_CTS NC +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_RXD NC +#define MBED_CONF_WNC14A2A_LIBRARY_WNC_TXD NC +#endif // !defined(TARGET_FF_ARDUINO) + +typedef struct smsmsg_t { + string number; + string date; + string time; + string msg; + } IOTSMS; + +typedef struct socket_t { + int socket; //index of this socket + string url; + SocketAddress addr; //address info for this socket + bool opened; //is the socket opened + bool connected; //is the socket connected + int proto; //this is a TCP or UDP socket + void (*_callback)(void*); //callback used with attach + void *_cb_data; //callback data to be returned + } WNCSOCKET; + +typedef struct rx_event_t { + int m_rx_wnc_state; //state of the socket receive + int m_rx_socket; //which socket is being rcvd on + uint8_t *m_rx_dptr; //pointer to the users data buffer + uint32_t m_rx_req_size; //Requested number of bytes to receive + uint32_t m_rx_total_cnt; //Total number of bytes received + int m_rx_timer; //Rx Timeout Timer + int m_rx_disTO; //Flag to disable Timeout Timer + void (*m_rx_callback)(void*); //callback used with attach + void *m_rx_cb_data; //callback data to be returned + uint32_t m_rx_return_cnt; //number of bytes the Event Queue is returning + } RXEVENT; + +typedef struct tx_event_t { + // Transmit Interrupt simulation to enabled non-blocking operation + int m_tx_wnc_state; + int m_tx_socket; + uint8_t *m_tx_dptr; + unsigned m_tx_orig_size; + uint32_t m_tx_req_size; + uint32_t m_tx_total_sent; + void (*m_tx_callback)(void*); + void *m_tx_cb_data; + } TXEVENT; + +#define WNC_DEBUG 0 //1=enable the WNC startup debug output + //0=disable the WNC startup debug output +#define STOP_ON_FE 1 //1=hang forever if a fatal error occurs + //0=simply return failed response for all socket calls +#define DISPLAY_FE 1 //1 to display the fatal error when it occurs + //0 to NOT display the fatal error +#define RESETON_FE 0 //1 to cause the MCU to reset on fatal error + //0 to NOT reset the MCU + +#define APN_DEFAULT "m2m.com.attz" + +/** Error Handling macros & data +* @brief The macros CHK_WNCFE is used to check if a fatal error has occured. If it has +* then execute the action specified: fail, void, null, resume +* +* CHK_WNCFE( condition-to-check, fail|void|null|resume ) +* +* 'fail' if you want FATAL_WNC_ERROR to be called. +* 'void' if you want to execute a void return +* 'null' if you want to execute a null return +* 'resume' if you simply want to resume program execution +* +* There are several settings that control how FATAL_WNC_ERROR behaves: +* 1) RESETON_FE determines if the system will reset or hang. +* 2) DISPLAY_FE determine if an error message is generated or not +* +* The DISPLAY_FE setting determines if a failure message is displayed. +* If set to 1, user sees this messageo: +* +* WNC FAILED @ source-file-name:source-file-line-number +* +* if not set, nothing is displayed. +*/ + +#define FATAL_FLAG WncController::WNC_NO_RESPONSE +#define WNC_GOOD WncController::WNC_ON + +#define RETfail return -1 +#define RETvoid return +#define RETnull return NULL +#define RETresume + +#define DORET(x) RET##x + +#define TOSTR(x) #x +#define INTSTR(x) TOSTR(x) +#define FATAL_STR (char*)(__FILE__ ":" INTSTR(__LINE__)) + +#if RESETON_FE == 1 //reset on fatal error +#define MCURESET ((*((volatile unsigned long *)0xE000ED0CU))=(unsigned long)((0x5fa<<16) | 0x04L)) +#define RSTMSG "RESET MCU! " +#else +#define MCURESET +#define RSTMSG "" +#endif + +#if DISPLAY_FE == 1 //display fatal error message +#define PFE {if(_debugUart)_debugUart->printf((char*)RSTMSG "\r\n>>WNC FAILED @ %s\r\n", FATAL_STR);} +#else +#define PFE +#endif + +#if STOP_ON_FE == 1 //halt cpu on fatal error +#define FATAL_WNC_ERROR(v) {_fatal_err_loc=FATAL_STR;PFE;MCURESET;while(1);} +#else +#define FATAL_WNC_ERROR(v) {_fatal_err_loc=FATAL_STR;PFE;DORET(v);} +#endif + +#define CHK_WNCFE(x,y) if( x ){FATAL_WNC_ERROR(y);} + +#define FIRMWARE_REV(x) (((WNC14A2AInterface*)x)->getWNCRev()) +#define DBGMSG_DRV 0x04 +#define DBGMSG_EQ 0x08 +#define DBGMSG_SMS 0x10 +#define DBGMSG_ARRY 0x20 + +#define MAX_SMS_MSGS 3 + +using namespace WncController_fk; + +/** WNC14A2AInterface class + * Implementation of the NetworkInterface for WNC14A2A + */ +class WNC14A2AInterface : public NetworkStack, public NetworkInterface +{ +public: + + /** WNC14A2AInterface Constructor. + * @param optionally include a pointer to WNCDEBUG object for + * debug information to be displayed. + */ + WNC14A2AInterface(WNCDebug *_dbgUart = NULL); + virtual ~WNC14A2AInterface(); + + /** Set the cellular network credentials + * + * @param apn Optional, APN of network + * @param user Optional, username --not used-- + * @param pass Optional, password --not used-- + * @return nsapi_error_t + */ + virtual nsapi_error_t set_credentials(const char *apn = 0, + const char *username = 0, const char *password = 0); + + /** Connect to the network + * + * @param apn Optional, APN of network + * @param user Optional, username --not used-- + * @param pass Optional, password --not used-- + * @return nsapi_error_t + */ + virtual nsapi_error_t connect(const char *apn, + const char *username = 0, const char *password = 0); + + /** Connect to the network (no parameters) + * + * @return nsapi_error_t + */ + virtual nsapi_error_t connect(); + + /** disconnect from the network + * + * provided for completness, but function does nothing becase + * WNC part can not disconnect from network once connected. + * + * @return nsapi_error_t + */ + virtual nsapi_error_t disconnect(); + + /** Get the IP address of WNC device. From NetworkStack Class + * + * @return IP address string or null + */ + virtual const char *get_ip_address(); + + /** Get the network assigned IP address. + * + * @return IP address or null + */ + const char *get_my_ip_address(); + + /** Get the MAC address of the WNC device. + * + * @return MAC address of the interface + */ + virtual const char *get_mac_address(); + + /** Attach a callback function for when a SMS is recevied + * + * @param function pointer to call + */ + void sms_attach(void (*callback)(IOTSMS *)); + + /** Set the level of Debug output + * + * @param bit field + * basic AT command info= 0x01 + * more AT command info = 0x02 + * mbed driver info = 0x04 + * dump buffers = 0x08 + * all debug = 0x0f + */ + void doDebug(int v); + + /** Return the WNC reported Firmware Revision + * + * @param none. + */ + const char* getWNCRev(void); + + /** Query registered state of WNC + * + * @return true if registerd, false if not + */ + bool registered(); + + /** Start the SMS monitoring service + */ + void sms_start(void); + + /** start listening for incomming SMS messages + * + * @param time in msec to check + */ + void sms_listen(uint16_t=1000); // Configure device to listen for text messages + + /** retrieve a SMS message + * + * @param pointer to an array of IOTSMS messages + */ + int getSMS(IOTSMS **msg); + + /** send a SMS message + * + * @param a string containing number to send message to + * @param a string containing message to send + * @return true on success, 0 on failure + */ + int sendIOTSms(const string&, const string&); + + /** return this devices SMS number + * + * @brief The IOTSMS number used, isn't phone number, it is device ICCID. + * + * @return this devices IOTSMS number + */ + char* getSMSnbr(); + + +protected: + + /** Get Host IP by name. + * + * @return nsapi_error_t + */ + virtual nsapi_error_t gethostbyname(const char* name, SocketAddress *address, nsapi_version_t version); + + + /** return a pointer to the NetworkStack object + * + * @return The underlying NetworkStack object + */ + virtual NetworkStack *get_stack(); + + /** Open a socket. + * + * @param handle Handle in which to store new socket + * @param proto Type of socket to open, NSAPI_TCP or NSAPI_UDP + * @return 0 on success, negative on failure + */ + virtual int socket_open(void **handle, nsapi_protocol_t proto); + + /** Close the socket. + * + * @param handle Socket handle + * @return 0 on success, negative on failure + */ + virtual int socket_close(void *handle); + + /** Bind a server socket to a specific port. + * + * @brief Bind the socket to a specific port + * @param handle Socket handle + * @param address address to listen for + * @return 0; + */ + virtual int socket_bind(void *handle, const SocketAddress &address); + + /** Start listening for incoming connections. + * + * @brief NOT SUPPORTED + * @param handle Socket handle + * @param backlog Number of pending connections that can be queued up at any + * one time [Default: 1] + * @return NSAPI_ERROR_UNSUPPORTED; + * @note This function causes the receive time-out to be ignored resulting in continuous + * monitoring of received data. This is non-standard behaviour but it is how the + * mbed-cloud servcies use it... + */ + virtual int socket_listen(void *handle, int backlog); + + /** Accept a new connection. + * + * @brief NOT SUPPORTED + * @return NSAPI_ERROR_UNSUPPORTED; + */ + virtual int socket_accept(nsapi_socket_t server, + nsapi_socket_t *handle, SocketAddress *address=0); + + /** Connects this socket to the server. + * + * @param handle Socket handle + * @param address SocketAddress + * @return 0 on success, negative on failure + */ + virtual int socket_connect(void *handle, const SocketAddress &address); + + /** Send data to the remote host. + * + * @param handle Socket handle + * @param data buffer to send + * @param size length of buffer + * @return Number of bytes written or negative on failure + * + * @note This call is blocking. + */ + virtual int socket_send(void *handle, const void *data, unsigned size); + + /** Receive data from the remote host. + * + * @param handle Socket handle + * @param data buffer to store the recived data + * @param size bytes to receive + * @return received bytes received, negative on failure + * + * @note This call is not-blocking + */ + virtual int socket_recv(void *handle, void *data, unsigned size); + + /** Send a packet to a remote endpoint. + * + * @param handle Socket handle + * @param address SocketAddress + * @param data data to send + * @param size number of bytes to send + * @return the number of bytes sent or negative on failure + * + * @note This call is blocking. + */ + virtual int socket_sendto(void *handle, const SocketAddress &address, const void *data, unsigned size); + + /** Receive packet remote endpoint + * + * @param handle Socket handle + * @param address SocketAddress + * @param buffer buffer to store data to + * @param size number of bytes to receive + * @return the number bytes received or negative on failure + * + * @note This call is not-blocking. + */ + virtual int socket_recvfrom(void *handle, SocketAddress *address, void *buffer, unsigned size); + + /** Register a callback on state change of the socket + * + * @param handle Socket handle + * @param callback Function to call on state change + * @param data Argument to pass to callback + * + * @note Callback may be called in an interrupt context. + */ + virtual void socket_attach(void *handle, void (*callback)(void *), void *data); + + /** get the status of internal errors + * + * @brief Called after any WNC14A2A operation to determine error specifics + * @param none. + */ + uint16_t wnc14a2a_chk_error(void) { return m_errors; } + +private: + + //! WncController Class for interacting with the 14A2a hardware + friend class WncControllerK64F; + + bool m_wncpoweredup; //track if WNC has been power-up + unsigned m_debug; + + WncIpStats myNetStats; //maintaint the network statistics + WncControllerK64F_fk::WncControllerK64F *m_pwnc; //pointer to the WncController instance + + WNCDebug *_debugUart; // Serial object for parser to communicate with radio + char *_fatal_err_loc; // holds string containing location of fatal error + nsapi_error_t m_errors; + + bool m_smsmoning; // Track if the SMS monitoring thread is running + EventQueue sms_queue; // Queue used to schedule for SMS checks + void (*_sms_cb)(IOTSMS *); // Callback when text message is received. User must define this as + // a static function because I'm not handling an object offset + IOTSMS m_MsgText, m_MsgText_array[MAX_SMS_MSGS]; // Used to pass SMS message to the user + struct WncController::WncSmsList m_smsmsgs; //use the WncSmsList structure to hold messages + + EventQueue wnc_queue; // Queue used to schedule for receiving data + void handle_sms_event(); // SMS tx/rx handler + void wnc_eq_event(); // Simulated ISR + int rx_event(RXEVENT *); // receive data handler + int tx_event(TXEVENT *); // tx data handler + + char _mac_address[NSAPI_MAC_SIZE]; // local Mac + void _dbOut(const char *format, ...); + void _dbDump_arry( const uint8_t* data, unsigned int size ); + + WNCSOCKET _sockets[WNC14A2A_SOCKET_COUNT]; //WNC supports 8 open sockets but driver only supports 1 currently + TXEVENT _socTxS[WNC14A2A_SOCKET_COUNT]; + RXEVENT _socRxS[WNC14A2A_SOCKET_COUNT]; + Thread _smsThread, _eqThread; //Event Queue thread for SMS and Rx/Tx data + Mutex _pwnc_mutex; + int _active_socket; + + UARTSerial mdmUart; + WncIO wnc_io; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/WNC14A2AInterface/WNCDebug.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,99 @@ +/** +* copyright (c) 2017-2018, James Flynn +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** +* @file WNCDebug.h +* @brief A debug class that coordinates the output of debug messages to +* either stdio or a serial port based on instantiation. +* +* @author James Flynn +* +* @date 1-Feb-2018 +* +*/ + +#ifndef __WNCDEBUG__ +#define __WNCDEBUG__ +#include <stdio.h> +#include "WNCIO.h" + +/** WNCDebug class +* Used to write debug data to the user designated IO. Currently +* The class expects either a stdio element (defaults to stderr) or +* a pointer to a WncIO object. +*/ + +class WNCDebug +{ +public: + //! Create class with either stdio or a pointer to a uart + WNCDebug( FILE * fd = stderr ): m_puart(NULL) {m_stdiofp=fd;}; + WNCDebug( WncIO * uart): m_stdiofp(NULL) {m_puart=uart;}; + ~WNCDebug() {}; + + //! standard printf() functionallity + int printf( char * fmt, ...) { + char buffer[256]; + int ret=0; + va_list args; + va_start (args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + prt.lock(); + if( m_stdiofp ) + ret=fputs(buffer,m_stdiofp); + else + ret=m_puart->puts(buffer); + prt.unlock(); + va_end (args); + return ret; + } + + //! standard putc() functionallity + int putc( int c ) { + int ret=0; + prt.lock(); + if( m_stdiofp ) + ret=fputc(c, m_stdiofp); + else + ret=m_puart->putc(c); + prt.unlock(); + return ret; + } + + //! standard puts() functionallity + int puts( const char * str ) { + int ret=0; + prt.lock(); + if( m_stdiofp ) + ret=fputs(str,m_stdiofp); + else + ret=m_puart->puts(str); + prt.unlock(); + return ret; + } + +private: + std::FILE *m_stdiofp; + WncIO *m_puart; + Mutex prt; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/WNC14A2AInterface/WNCIO.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,102 @@ +/** +* copyright (c) 2017-2018, James Flynn +* SPDX-License-Identifier: Apache-2.0 +*/ + +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +/** +* @file WNCIO.h +* @brief A class that WNCInterface uses for input/output +* +* @author James Flynn +* +* @date 1-Feb-2018 +* +*/ + +#ifndef __WNCIO__ +#define __WNCIO__ +#include <stdio.h> +#include "mbed.h" + +/** WncIO class +* Used to read/write the WNC UART using FILE I/O. +*/ + +class WncIO +{ +public: + //! Create class with either stdio or a pointer to a uart + WncIO( UARTSerial * uart): m_puart(uart) {;} + ~WncIO() {}; + + //! standard printf() functionallity + int printf( char * fmt, ...) { + char buffer[256]; + int ret=0; + va_list args; + va_start (args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + prt.lock(); + ret=m_puart->write(buffer,strlen(buffer)); + prt.unlock(); + va_end (args); + return ret; + } + + //! standard putc() functionallity + int putc( int c ) { + int ret=0; + prt.lock(); + ret=m_puart->write((const void*)&c,1); + prt.unlock(); + return ret; + } + + //! standard puts() functionallity + int puts( const char * str ) { + int ret=0; + prt.lock(); + ret=m_puart->write(str,strlen(str)); + prt.unlock(); + return ret; + } + + //! return true when data is available, false otherwise + bool readable( void ) { + return m_puart->readable(); + } + + //! get the next character available from the uart + int getc( void ) { + char c; + m_puart->read( &c, 1 ); + return c; + } + + //! set the uart baud rate + void baud( int baud ) { + m_puart->set_baud(baud); + } + +private: + UARTSerial *m_puart; + Mutex prt; +}; + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/WNC14A2AInterface/WncControllerK64F/WncController/WncController.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,2154 @@ +/* + Copyright (c) 2016 Fred Kellerman + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + @file WncController.cpp + @purpose Controls WNC 14A2A Cellular Modem + @version 1.0 + @date July 2016 + @author Fred Kellerman + + + An Example of usage: + + WncControllerK64F mdm(&wncPinList, &mdmUart, &debugUart); + + mdm.enableDebug(true, true); + + if (false == mdm.powerWncOn("m2m.com.attz", 60)) { + while(1); + } + + // ICCID and MSISDN + string iccid; string msisdn; + if (mdm.getICCID(&iccid) == true) { + if (mdm.convertICCIDtoMSISDN(iccid, &msisdn) == true) { + // Send an SMS message (must use 15-digit MISDN number!) + mdm.sendSMSText(msisdn.c_str(), "Hello from WNC Kit -> from WNC"); + } + } + + // Get an IP address setup for the socket #1 (0 indexed)) + if (true == mdm.resolveUrl(0, "www.att.com")) + { + // Report server IP + if (true == mdm.getIpAddr(0, ipAddrStr)) { + debugUart.puts("Server IP: "); + debugUart.puts(ipAddrStr); + debugUart.puts("\r\n"); + } + + // Open Socket #1, TCP=true resolved IP on port 80: + if (true == mdm.openSocket(0, 80, true)) { + // Write some data + const uint8_t * dataStr = "GET /index.html HTTP/1.0\r\nFrom: someuser@someuser.com\r\nUser-Agent: HTTPTool/1.0\r\n\r\n"; + if (true == mdm.write(0, dataStr, strlen((const char *)dataStr))) + { + const uint8_t * myBuf; + mdm.setReadRetries(0, 20); + uint32_t n = mdm.read(0, &myBuf); + if (n > 0) + debugUart.printf("Read %d chars: %s\r\n", n, myBuf); + else + debugUart.puts("No read data!\r\n"); + } + } + } + +*/ + + +#include <cstdlib> +#include <cctype> +#include <string.h> +#include "WncController.h" + +namespace WncController_fk { + +///////////////////////////////////////////////////// +// Static initializers +///////////////////////////////////////////////////// +WncController::WncSocketInfo_s WncController::m_sSock[MAX_NUM_WNC_SOCKETS]; +const WncController::WncSocketInfo_s WncController::defaultSockStruct = { 0, false, "192.168.0.1", 80, 0, 25, true, 30 }; + +WncController::WncState_e WncController::m_sState = WNC_OFF; +uint16_t WncController::m_sCmdTimeoutMs = WNC_CMD_TIMEOUT_MS; +string WncController::m_sApnStr = "NULL"; +string WncController::m_sWncStr; +string WncController::m_FirmwareRevision; +uint8_t WncController::m_sPowerUpTimeoutSecs = MAX_POWERUP_TIMEOUT; +bool WncController::m_sDebugEnabled = false; +bool WncController::m_sMoreDebugEnabled = false; +bool WncController::m_sCheckNetStatus = false; // Turn on internet status check between every command +const char * const WncController::INVALID_IP_STR = ""; +bool WncController::m_sReadyForSMS = false; + + +/** + * C++ version 0.4 char* style "itoa": + * Written by Lukás Chmela + * Released under GPLv3. + */ +static char* itoa(int64_t value, char* result, int base) +{ + // check that the base is valid + if ( base < 2 || base > 36 ) { + *result = '\0'; + return result; + } + + char* ptr = result, *ptr1 = result, tmp_char; + int64_t tmp_value; + + do { + tmp_value = value; + value /= base; + *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + (tmp_value - value * base)]; + } while ( value ); + + // Apply negative sign + if ( tmp_value < 0 ) + *ptr++ = '-'; + + *ptr-- = '\0'; + + while ( ptr1 < ptr ) { + tmp_char = *ptr; + *ptr-- = *ptr1; + *ptr1++ = tmp_char; + } + + return result; +} + +const char * WncController::_to_string(int64_t value) +{ + static char str[21]; // room for signed 64-bit + null + itoa(value, str, 10); + return (str); +} + +const char * WncController::_to_hex_string(uint8_t value) +{ + static char str[3]; // room for 8-bit + null + itoa(value, str, 16); + return (str); +} + +WncController::WncController(void) +{ + for(unsigned i=0; i<MAX_NUM_WNC_SOCKETS; i++) + m_sSock[i] = defaultSockStruct; +} + +WncController::~WncController(void) {}; + +const char* WncController::getFirmRev(void) +{ + return m_FirmwareRevision.c_str(); +} + +void WncController::enableDebug(bool on, bool moreDebugOn) +{ + m_sDebugEnabled = on; + m_sMoreDebugEnabled = moreDebugOn; +} + +WncController::WncState_e WncController::getWncStatus(void) +{ + return (m_sState); +} + +int16_t WncController::getDbmRssi(void) +{ + int16_t rssi, ber; + if (at_getrssiber_wnc(&rssi, &ber) == true) + return (rssi); + else + return (99); +} + +int16_t WncController::get3gBer(void) +{ + int16_t rssi, ber; + if (at_getrssiber_wnc(&rssi, &ber) == true) + return (ber); + else + return (99); +} + +bool WncController::powerWncOn(const char * const apn, uint8_t powerUpTimeoutSecs) +{ + dbgPuts("Waiting for WNC to Initialize..."); + m_sPowerUpTimeoutSecs = powerUpTimeoutSecs; + m_sState = WNC_ON_NO_CELL_LINK; // Turn soft on to allow "AT" for init to be sent! + if (initWncModem(powerUpTimeoutSecs) == true) { + // Set the Apn + setApnName(apn); + if (false == softwareInitMdm()) { + dbgPuts("Software init failed!"); + m_sState = WNC_OFF; + } + } + else { + dbgPuts("Power up failed!"); + m_sState = WNC_OFF; + } + + return ((m_sState == WNC_ON) || (m_sState == WNC_ON_NO_CELL_LINK)); +} + +size_t WncController::sendCustomCmd(const char * cmd, char * resp, size_t sizeRespBuf, int ms_timeout) +{ + string * respStr; + + if (sizeRespBuf > 0) { + at_send_wnc_cmd(cmd, &respStr, ms_timeout); + strncpy(resp, respStr->c_str(), sizeRespBuf); + if (respStr->size() > sizeRespBuf) + dbgPuts("sendCustomCmd truncated!"); + + return (respStr->size()); + } + + dbgPuts("sendCustomCmd: would have overrun!"); + + return (0); +} + +bool WncController::pingUrl(const char * url) +{ + string ipAddr; + + if (true == at_dnsresolve_wnc(url, &ipAddr)) + return (pingIp(ipAddr.c_str())); + else + dbgPuts("pingUrl DNS resolve: failed!"); + + return (false); +} + +bool WncController::pingIp(const char * ip) +{ + if (true == at_ping_wnc(ip)) + return (true); + else + dbgPuts("pingIp: failed!"); + + return (false); +} + +bool WncController::getWncNetworkingStats(WncIpStats * s) +{ + return (at_get_wnc_net_stats(s)); +} + +bool WncController::getIpAddr(uint16_t numSock, char myIpAddr[MAX_LEN_IP_STR]) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) { + strncpy(myIpAddr, m_sSock[numSock].myIpAddressStr.c_str(), MAX_LEN_IP_STR); + myIpAddr[MAX_LEN_IP_STR - 1] = '\0'; + return (true); + } + else { + myIpAddr[0] = '\0'; + return (false); + } +} + +bool WncController::setApnName(const char * const apnStr) +{ + if (at_setapn_wnc(apnStr) == true) + { + m_sApnStr = apnStr; + return (true); + } + else + return (false); +} + +bool WncController::resolveUrl(uint16_t numSock, const char * url) +{ + bool cmdRes; + + if (numSock < MAX_NUM_WNC_SOCKETS) { + if (strlen(url) > 0) { + cmdRes = at_dnsresolve_wnc(url, &m_sSock[numSock].myIpAddressStr); + if (cmdRes == false) + dbgPuts("Cannot resolve URL!"); + return (cmdRes); + } + else + dbgPuts("Invalid URL"); + } + else + dbgPuts("Invalid Sock num!"); + + return (false); +} + +bool WncController::setIpAddr(uint16_t numSock, const char * ipStr) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) { + m_sSock[numSock].myIpAddressStr = ipStr; + return (true); + } + else { + dbgPuts("Bad socket num!"); + return (false); + } +} + +void WncController::setWncCmdTimeout(uint16_t toMs) +{ + m_sCmdTimeoutMs = toMs; +} + +bool WncController::openSocketUrl(uint16_t numSock, const char * url, uint16_t port, bool tcp, uint16_t timeOutSec) +{ + if (resolveUrl(numSock, url) == true) + return (openSocket(numSock, port, tcp, timeOutSec)); + + return (false); +} + +bool WncController::openSocketIpAddr(uint16_t numSock, const char * ipAddr, uint16_t port, bool tcp, uint16_t timeOutSec) +{ + if (setIpAddr(numSock, ipAddr) == true) + return (openSocket(numSock, port, tcp, timeOutSec)); + + return (false); +} + +bool WncController::openSocket(uint16_t numSock, uint16_t port, bool tcp, uint16_t timeOutSec) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) { + // IPV4 ip addr sanity check! + size_t lenIpStr = m_sSock[numSock].myIpAddressStr.size(); + if (lenIpStr < 7 || lenIpStr > 15) { + dbgPuts("Invalid IP Address!"); + return (false); + } + + // Already open ? Must close if want to re-open with new settings. + if (m_sSock[numSock].open == true) { + dbgPuts("Socket already open, close then re-open!"); + if (true == at_sockclose_wnc(m_sSock[numSock].numWncSock)) + m_sSock[numSock].open = false; + else + return (false); + } + + m_sSock[numSock].myPort = port; + m_sSock[numSock].isTcp = tcp; + m_sSock[numSock].timeOutSec = timeOutSec; + + int16_t numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), port, numSock, tcp, timeOutSec); + m_sSock[numSock].numWncSock = numWncSock; + if (numWncSock > 0 && numWncSock <= (uint16_t)MAX_NUM_WNC_SOCKETS) + m_sSock[numSock].open = true; + else { + m_sSock[numSock].open = false; + dbgPuts("Socket open fail!!!!"); + + // If the modem is not responding don't bother it. + if (WNC_NO_RESPONSE != getWncStatus()) { + // Work-around. If the sock open fails it needs to be told + // to close. If 6 sock opens happen with a fail, it further + // crashes the WNC. Not sure why the sock won't open. + at_sockclose_wnc(m_sSock[numSock].numWncSock); + } + } + } + else { + dbgPuts("Bad socket num or IP!"); + return (false); + } + + return (m_sSock[numSock].open); +} + +bool WncController::sockWrite(const uint8_t * const s, uint16_t n, uint16_t numSock, bool isTcp) +{ + bool result = true; + + AtCmdErr_e cmdRes = at_sockwrite_wnc(s, n, m_sSock[numSock].numWncSock, isTcp); + if (cmdRes != WNC_AT_CMD_OK) { + if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME)) + { + // This may throw away any data that hasn't been written out of the WNC + // but at this point with the way the WNC currently works we have + // no choice. + closeOpenSocket(numSock); + } + result = false; + } + + return (result); +} + +bool WncController::write(uint16_t numSock, const uint8_t * s, uint32_t n) +{ + bool result; + + if (numSock < MAX_NUM_WNC_SOCKETS) { + if (m_sSock[numSock].open == true) { + if (n <= MAX_WNC_WRITE_BYTES) { + result = sockWrite(s, n, numSock, m_sSock[numSock].isTcp); + } + else { + uint16_t rem = n % MAX_WNC_WRITE_BYTES; + while (n >= MAX_WNC_WRITE_BYTES) { + n -= MAX_WNC_WRITE_BYTES; + result = sockWrite(s, MAX_WNC_WRITE_BYTES, numSock, m_sSock[numSock].isTcp); + if (result == false) { + n = 0; + rem = 0; + dbgPuts("Sock write fail!"); + } + else + s += MAX_WNC_WRITE_BYTES; + } + if (rem > 0) + result = sockWrite(s, rem, numSock, m_sSock[numSock].isTcp); + } + } + else { + dbgPuts("Socket is closed for write!"); + result = false; + } + } + else { + dbgPuts("Bad socket num!"); + result = false; + } + + return (result); +} + +size_t WncController::read(uint16_t numSock, const uint8_t ** readBuf) +{ + static string theBuf; + string readStr; + + theBuf.erase(); // Clean-up from last time + + if (numSock < MAX_NUM_WNC_SOCKETS) { + if (m_sSock[numSock].open == true) { + uint8_t i = m_sSock[numSock].readRetries; + uint16_t to = m_sSock[numSock].readRetryWaitMs; + bool foundData = false; + do { + AtCmdErr_e cmdRes; + cmdRes = at_sockread_wnc(&readStr, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp); + if (WNC_AT_CMD_OK == cmdRes) { + // This will let this loop read until the socket data is + // empty. If no data, then wait the retry amount of time. + if (readStr.size() > 0) { + theBuf += readStr; + foundData = true; + i = 1; + } + else { + // Once data is found start returning it asap + if (foundData == false) + waitMs(to); + } + } + else { + theBuf += readStr; // Append what if any we got before it errored. + dbgPuts("Sockread failed!"); + if (WNC_NO_RESPONSE == getWncStatus()) { + i = 0; + } + else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME)) + { + // This may throw away any data that hasn't been read out of the WNC + // but at this point with the way the WNC currently works we have + // no choice. + closeOpenSocket(numSock); + i = 0; + } + else + waitMs(to); + } + } while (i-- > 0); + } + else { + dbgPuts("Socket is closed for read"); + } + } + else { + dbgPuts("Bad socket num!"); + } + + *readBuf = (const uint8_t *)theBuf.c_str(); + + return (theBuf.size()); +} + +size_t WncController::read(uint16_t numSock, uint8_t * readBuf, uint32_t maxReadBufLen) +{ + uint32_t numCopied = 0; + + if (numSock < MAX_NUM_WNC_SOCKETS) { + if (m_sSock[numSock].open == true) { + uint8_t i = m_sSock[numSock].readRetries; + uint16_t to = m_sSock[numSock].readRetryWaitMs; + bool foundData = false; + uint16_t numRead; + do { + AtCmdErr_e cmdRes; + if (maxReadBufLen < MAX_WNC_READ_BYTES) + cmdRes = at_sockread_wnc(readBuf, &numRead, maxReadBufLen, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp); + else + cmdRes = at_sockread_wnc(readBuf, &numRead, MAX_WNC_READ_BYTES, m_sSock[numSock].numWncSock, m_sSock[numSock].isTcp); + + if (WNC_AT_CMD_OK == cmdRes) { + // This will let this loop read until the socket data is + // empty. If no data, then wait the retry amount of time. + if (numRead > 0) { + foundData = true; + i = 1; + if (numRead <= maxReadBufLen) { + maxReadBufLen -= numRead; + numCopied += numRead; + readBuf += numRead; + } + else { + i = 0; // No more room for data! + dbgPutsNoTime("No more room for read data!"); + } + } + else { + // Once data is found start returning it asap + if (foundData == false) + waitMs(to); + } + } + else { + dbgPuts("Sockread failed!"); + if (WNC_NO_RESPONSE == getWncStatus()) { + i = 0; + } + else if ((cmdRes == WNC_AT_CMD_ERREXT) || (cmdRes == WNC_AT_CMD_ERRCME)) + { + // This may throw away any data that hasn't been read out of the WNC + // but at this point with the way the WNC currently works we have + // no choice. + closeOpenSocket(numSock); + i = 0; + } + else + waitMs(to); + } + } while ((i-- > 0) && (maxReadBufLen > 0)); + } + else { + dbgPuts("Socket is closed for read"); + } + } + else { + dbgPuts("Bad socket num!"); + } + + return (numCopied); +} + +void WncController::setReadRetries(uint16_t numSock, uint16_t retries) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) + m_sSock[numSock].readRetries = retries; + else + dbgPuts("Bad socket num!"); +} + +void WncController::setReadRetryWait(uint16_t numSock, uint16_t readRetryWaitMs) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) + m_sSock[numSock].readRetryWaitMs = readRetryWaitMs; + else + dbgPuts("Bad socket num!"); +} + +bool WncController::closeSocket(uint16_t numSock) +{ + if (numSock < MAX_NUM_WNC_SOCKETS) { + + if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock)) + dbgPuts("Sock close may not have closed!"); + + // Even with an error the socket could have closed, + // can't tell for sure so just soft close it for now. + m_sSock[numSock].open = false; + } + else { + dbgPuts("Bad socket num!"); + } + + return (m_sSock[numSock].open == false); +} + +size_t WncController::mdmGetline(string * buff, int timeout_ms) +{ + char chin = '\0'; + char chin_last; + size_t len = 0; + + startTimerB(); + while ((len <= MAX_LEN_WNC_CMD_RESPONSE) && (getTimerTicksB_mS() < timeout_ms)) { + if (charReady()) { + chin_last = chin; + chin = getc(); + if (isprint(chin)) { + *buff += chin; + len++; // Bound the copy length to something reaonable just in case + continue; + } + else if ((('\r' == chin_last) && ('\n' == chin)) || (('\n' == chin_last) && ('\r' == chin))) { + break; + } + } + } + stopTimerB(); + + if (len > MAX_LEN_WNC_CMD_RESPONSE) + dbgPuts("Max cmd length reply exceeded!"); + + return (len); +} + +bool WncController::softwareInitMdm(void) +{ + static bool reportStatus = true; + unsigned i; + + if (checkCellLink() == true) { + if (reportStatus == false) { + dbgPuts("Re-connected to cellular network!"); + reportStatus = true; + } + + // WNC has SIM and registered on network so + // soft initialize the WNC. + for (i = 0; i < WNC_SOFT_INIT_RETRY_COUNT; i++) + if (at_init_wnc() == true) + break; + + // If it did not respond try a hardware init + if (i == WNC_SOFT_INIT_RETRY_COUNT) + { + at_reinitialize_mdm(); + return (at_init_wnc(true)); // Hard reset occurred so make it go through the software init(); + } + else + return (true); + } + else + { + if (reportStatus == true) { + dbgPuts("Not connected to cellular network!"); + reportStatus = false; + } + return (false); + } +} + +WncController::AtCmdErr_e WncController::sendWncCmd(const char * const s, string ** r, int ms_timeout) +{ + if (checkCellLink() == false) { + static string noRespStr; + + // Save some run-time! + if (m_sDebugEnabled) + { + dbgPuts("FAIL send cmd: ", false); + if (m_sMoreDebugEnabled && m_sDebugEnabled) { + dbgPutsNoTime(s); + } + else { + size_t n = strlen(s); + if (n <= WNC_TRUNC_DEBUG_LENGTH) { + dbgPutsNoTime(s); + } + else { + string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2); + truncStr += ".."; + truncStr += &s[n-(WNC_TRUNC_DEBUG_LENGTH/2)]; + dbgPutsNoTime(truncStr.c_str()); + } + } + } + + noRespStr.erase(); + *r = &noRespStr; + + return (WNC_AT_CMD_NO_CELL_LINK); + } + + if (m_sCheckNetStatus) + { + if (m_sMoreDebugEnabled) + dbgPuts("[---------- Network Status -------------"); + string * pRespStr; + at_send_wnc_cmd("AT@SOCKDIAL?", &pRespStr, m_sCmdTimeoutMs); + if (m_sMoreDebugEnabled) + dbgPuts("---------------------------------------]"); + } + + // If WNC ready, send user command + return (at_send_wnc_cmd(s, r, ms_timeout)); +} + +WncController::AtCmdErr_e WncController::at_send_wnc_cmd(const char * s, string ** r, int ms_timeout) +{ + // Save some run-time! + if (m_sDebugEnabled) + { + if (m_sMoreDebugEnabled) { + dbgPuts("TX: ", false); dbgPutsNoTime(s); + } + else { + if (m_sDebugEnabled) { // Save some run-time! + size_t n = strlen(s); + if (n <= WNC_TRUNC_DEBUG_LENGTH) { + dbgPuts("TX: ", false); dbgPutsNoTime(s); + } + else { + string truncStr(s,WNC_TRUNC_DEBUG_LENGTH/2); + truncStr += ".."; + truncStr += &s[n - (WNC_TRUNC_DEBUG_LENGTH/2)]; + dbgPuts("TX: ", false); dbgPutsNoTime(truncStr.c_str()); + } + } + } + } + + AtCmdErr_e atResult = mdmSendAtCmdRsp(s, ms_timeout, &m_sWncStr); + *r = &m_sWncStr; // Return a pointer to the static string + + if (atResult != WNC_AT_CMD_TIMEOUT) { + // If a prior command timed out but a new one works then + // change the state back to ON. We don't know here in this + // method if the Cell Link is good so assume it is. When a command + // that depends on the cell link is made it will update the state. + if (m_sState == WNC_NO_RESPONSE) + m_sState = WNC_ON; + + // Save some run-time! + if (m_sDebugEnabled) + { + dbgPuts("RX: ", false); + if (m_sMoreDebugEnabled) { + dbgPutsNoTime(m_sWncStr.c_str()); + } + else { + if (m_sWncStr.size() <= WNC_TRUNC_DEBUG_LENGTH) { + dbgPutsNoTime(m_sWncStr.c_str()); + } + else { + string truncStr = m_sWncStr.substr(0,WNC_TRUNC_DEBUG_LENGTH/2) + ".."; + truncStr += m_sWncStr.substr(m_sWncStr.size() - (WNC_TRUNC_DEBUG_LENGTH/2), WNC_TRUNC_DEBUG_LENGTH/2); + dbgPutsNoTime(truncStr.c_str()); + } + } + } + } + else { + m_sState = WNC_NO_RESPONSE; + dbgPuts("AT Cmd TIMEOUT!"); + dbgPuts("RX: ", false); dbgPutsNoTime(m_sWncStr.c_str()); + } + + return (atResult); +} + +void WncController::closeOpenSocket(uint16_t numSock) +{ + // Try to open and close the socket + do { + dbgPuts("Try to close and re-open socket"); + if (false == at_sockclose_wnc(m_sSock[numSock].numWncSock)) { + if (WNC_NO_RESPONSE == getWncStatus()) { + dbgPuts("No response for closeOpenSocket1"); + return ; + } + } + + int numWncSock = at_sockopen_wnc(m_sSock[numSock].myIpAddressStr.c_str(), m_sSock[numSock].myPort, numSock, m_sSock[numSock].isTcp, m_sSock[numSock].timeOutSec); + m_sSock[numSock].numWncSock = numWncSock; + if (numWncSock > 0 && numWncSock <= (int)MAX_NUM_WNC_SOCKETS) + m_sSock[numSock].open = true; + else { + m_sSock[numSock].open = false; + dbgPuts("Failed to re-open socket!"); + } + + if (WNC_NO_RESPONSE == getWncStatus()) { + dbgPuts("No response for closeOpenSocket2"); + return ; + } + } while (m_sSock[numSock].open == false); +} + +bool WncController::getICCID(string * iccid) +{ + if (at_geticcid_wnc(iccid) == false) { + dbgPuts("getICCID error!"); + return (false); + } + + return (true); +} + +bool WncController::at_geticcid_wnc(string * iccid) +{ + string * respStr; + + iccid->erase(); + + AtCmdErr_e r = at_send_wnc_cmd("AT%CCID", &respStr, m_sCmdTimeoutMs); + + if (r != WNC_AT_CMD_OK || respStr->size() == 0) + return (false); + + // New Firmware versions respond to the %CCID command with "%CCID:" + // but old version respond with "AT%CCID", so check to see which we have + size_t pos = respStr->find(":"); + if (pos == string::npos) + pos = respStr->find("AT%CCID"); + else + pos = respStr->find("%CCID"); + + if (pos == string::npos) + return (false); + + pos += 7; // Advanced to the number + + size_t posOK = respStr->rfind("OK"); + if (posOK == string::npos) + return (false); + + *iccid = respStr->substr(pos, posOK - pos); + + return (true); +} + +bool WncController::convertICCIDtoMSISDN(const string & iccid, string * msisdn) +{ + msisdn->erase(); + + if (iccid.size() != 20 && iccid.size() != 19) { + dbgPuts("Invalid ICCID length!"); + return (false); + } + + *msisdn = "882350"; + + if (iccid.size() == 20) + *msisdn += iccid.substr(10,iccid.size() - 11); + else + *msisdn += iccid.substr(10,iccid.size() - 10); + + return (true); +} + +bool WncController::sendSMSText(const char * const phoneNum, const char * const text) +{ + if (at_sendSMStext_wnc(phoneNum, text) == true) + return (true); + else { + dbgPuts("sendSMSText: Failed!"); + return (false); + } +} + +bool WncController::readSMSLog(struct WncSmsList * log) +{ + string * logStr; + uint16_t i; + + if (at_readSMSlog_wnc(&logStr) == false) { + dbgPuts("readSMSLog: Failed!"); + return (false); + } + + // Clean slate + log->msgCount = 0; + + if (logStr->size() == 0) + return (false); + + // Pick out the stuff from the string and convert to struct + string s; + size_t pos2; + size_t pos = logStr->find("+CMGL:"); + + for(i=0; i<MAX_WNC_SMS_MSG_SLOTS; i++) { + // Start with a clean slate, let parsing fill out later. + log->e[i].unread = false; + log->e[i].incoming = false; + log->e[i].unsent = false; + log->e[i].pduMode = false; + log->e[i].msgReceipt = false; + + log->e[i].idx = logStr->at(pos + 7); + if (pos == string::npos) + return (false); + pos2 = logStr->find(",\"", pos); + if (pos2 == string::npos) { + // If the WNC acts wrong and receives a PDU mode + // SMS there will not be any quotes in the response, + // just take the whole reply and make it the message body for + // now, mark it as an unread message, set the pdu flag! + log->e[log->msgCount].unread = true; + log->e[log->msgCount].pduMode = true; + log->msgCount++; + + pos2 = logStr->find("+CMGL", pos + 5); + if (pos2 == string::npos) { + pos2 = logStr->find("OK", pos + 5); + if (pos2 == string::npos) { + dbgPuts("Strange SMS Log Ending!"); + return (false); + } + i = MAX_WNC_SMS_MSG_SLOTS; + } + log->e[log->msgCount].msg = logStr->substr(0, pos2 - pos); + pos = pos2; // for loop starts off expecting pos to point to next log msg + continue; + } + pos += 2; // Advance to the text we want + pos2 = logStr->find("\",", pos); + if ((pos2 == string::npos) || (pos >= pos2)) + return (false); + + // Setup attributes + s = logStr->substr(pos, pos2 - pos); + if (s.find("REC READ") != string::npos) + log->e[i].incoming = true; + if (s.find("REC UNREAD") != string::npos) { + log->e[i].unread = true; + log->e[i].incoming = true; + } + if (s.find("STO UNSENT") != string::npos) + log->e[i].unsent = true; + if (logStr->find(",,") == string::npos) + log->e[i].msgReceipt = true; + + // Tele number + pos2 = logStr->find(",\"", pos2); + if (pos2 == string::npos) + return (false); + pos2 += 2; // Advance to next field + pos = logStr->find("\",", pos2); + if ((pos == string::npos) || (pos2 > pos)) + return (false); + if (pos == pos2) + log->e[i].number.erase(); + else + log->e[i].number = logStr->substr(pos2, pos - pos2); + + // Date + pos = logStr->find(",\"", pos); + if (pos == string::npos) + return (false); + pos += 2; // Beginning of date field + pos2 = logStr->find(",", pos); // End of timestamp field + if ((pos2 == string::npos) || (pos > pos2)) + return (false); + if (pos == pos2) + log->e[i].date.erase(); + else + log->e[i].date = logStr->substr(pos, pos2 - pos); + + // Timestamp + pos = logStr->find("\",", pos2); // End of timestamp + if (pos == string::npos) + return (false); + pos2 += 1; // Beginning of time field + if (pos < pos2) + return (false); + if (pos == pos2) + log->e[i].time.erase(); + else + log->e[i].time = logStr->substr(pos2, pos - pos2); + + // Message field + + // We don't know how many messages we have so the next search + // could end with +CMGL or OK. + pos += 2; // Advanced to message text + pos2 = logStr->find("+CMGL", pos); + if (pos2 == string::npos) { + pos2 = logStr->find("OK", pos); + if (pos2 == string::npos) { + dbgPuts("Strange SMS Log Ending!"); + return (false); + } + i = MAX_WNC_SMS_MSG_SLOTS; // break + } + if (pos > pos2) + return (false); + if (pos == pos2) + log->e[log->msgCount].msg.erase(); + else + log->e[log->msgCount].msg = logStr->substr(pos, pos2 - pos); + + log->msgCount++; // Message complete + } + + return (true); +} + +bool WncController::readUnreadSMSText(struct WncSmsList * w, bool deleteRead) +{ + struct WncController::WncSmsList tmp; + + if (readSMSLog(&tmp) == false) + return (false); + + w->msgCount = 0; + for(uint16_t i = 0; i < tmp.msgCount; i++) { + if (tmp.e[i].unread == true) { + w->e[w->msgCount] = tmp.e[i]; + w->msgCount++; + if (deleteRead == true) { + // Clean up message that was copied out and read + deleteSMSTextFromMem(w->e[i].idx); + } + } + } + + return (w->msgCount > 0); +} + +size_t WncController::getSignalQuality(const char ** log) +{ + size_t n; + + n = at_getSignalQuality_wnc(log); + if (n == 0) + dbgPuts("readSMSText: Failed!"); + + return (n); +} + +size_t WncController::at_getSignalQuality_wnc(const char ** log) +{ + string * pRespStr; + static string logStr; + + logStr.erase(); + + if (at_send_wnc_cmd("AT%MEAS=\"0\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr = *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=0: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"1\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=1: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"2\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=2: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"3\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=3: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"4\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=4: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"5\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=5: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"8\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=8: failed!"); + + if (at_send_wnc_cmd("AT%MEAS=\"98\"", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + logStr += *pRespStr; + logStr += "\r\n"; + } + else + dbgPuts("AT%MEAS=98: failed!"); + + *log = logStr.c_str(); + + return (logStr.size()); +} + +bool WncController::getTimeDate(struct WncDateTime * tod) +{ + if (at_gettimedate_wnc(tod) == true) + return (true); + else { + dbgPuts("Get time date failed!"); + return (false); + } +} + +bool WncController::at_ping_wnc(const char * ip) +{ + string * pRespStr; + string cmdStr = "AT@PINGREQ=\""; + cmdStr += ip; + cmdStr += "\""; + return (at_send_wnc_cmd(cmdStr.c_str(), &pRespStr, WNC_PING_CMD_TIMEOUT_MS) == WNC_AT_CMD_OK); +} + +bool WncController::at_gettimedate_wnc(struct WncDateTime * tod) +{ + string * pRespStr; + char * pEnd; + + if (at_send_wnc_cmd("AT+CCLK?", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) { + if (pRespStr->size() > 0) { + size_t pos1 = pRespStr->find("+CCLK:"); + if (pos1 != string::npos) { + pEnd = (char *)pRespStr->c_str() + pos1 + 8; + tod->year = strtol(pEnd, &pEnd, 10); + tod->month = strtol(pEnd+1, &pEnd, 10); + tod->day = strtol(pEnd+1, &pEnd, 10); + tod->hour = strtol(pEnd+1, &pEnd, 10); + tod->min = strtol(pEnd+1, &pEnd, 10); + tod->sec = strtol(pEnd+1, &pEnd, 10); + return (true); + } + } + } + + return (false); +} + +bool WncController::at_get_wnc_net_stats(WncIpStats * s) +{ + string * pRespStr; + AtCmdErr_e cmdRes = at_send_wnc_cmd("AT+CGCONTRDP=1", &pRespStr, m_sCmdTimeoutMs); + + if (WNC_AT_CMD_OK == cmdRes) { + if (pRespStr->size() > 0) { + memset((void*)s, '\0', sizeof(*s)); // Clean-up + string ss; + size_t pe; + size_t ps = pRespStr->rfind("\""); + if (ps != string::npos) { + ps += 2; // Skip the , after the " + pe = ps; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + + ss = pRespStr->substr(ps, pe - 1 - ps); + strncpy(s->ip, ss.c_str(), MAX_LEN_IP_STR); + s->ip[MAX_LEN_IP_STR - 1] = '\0'; + ps = pe; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(",", pe); + + ss = pRespStr->substr(ps, pe - ps); + strncpy(s->mask, ss.c_str(), MAX_LEN_IP_STR); + s->mask[MAX_LEN_IP_STR - 1] = '\0'; + ps = pe + 1; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(",", pe); + + ss = pRespStr->substr(ps, pe - ps); + strncpy(s->gateway, ss.c_str(), MAX_LEN_IP_STR); + s->gateway[MAX_LEN_IP_STR - 1] = '\0'; + ps = pe + 1; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(",", pe); + + + ss = pRespStr->substr(ps, pe - ps); + strncpy(s->dnsPrimary, ss.c_str(), MAX_LEN_IP_STR); + s->dnsPrimary[MAX_LEN_IP_STR - 1] = '\0'; + ps = pe + 1; + + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(".", pe); + if (pe == string::npos) + return (false); + else + pe += 1; + pe = pRespStr->find(",", pe); + + + ss = pRespStr->substr(ps, pe - ps); + strncpy(s->dnsSecondary, ss.c_str(), MAX_LEN_IP_STR); + s->dnsSecondary[MAX_LEN_IP_STR - 1] = '\0'; + + dbgPuts("~~~~~~~~~~ WNC IP Stats ~~~~~~~~~~~~"); + dbgPuts("ip: ", false); dbgPutsNoTime(s->ip); + dbgPuts("mask: ", false); dbgPutsNoTime(s->mask); + dbgPuts("gateway: ", false); dbgPutsNoTime(s->gateway); + dbgPuts("dns pri: ", false); dbgPutsNoTime(s->dnsPrimary); + dbgPuts("dns sec: ", false); dbgPutsNoTime(s->dnsSecondary); + dbgPuts("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); + + return (true); + } + } + } + + return (false); +} + +bool WncController::deleteSMSTextFromMem(char msgIdx) +{ + const char * err = "deleteSMSTextFromMem: Failed!"; + + switch (msgIdx) + { + case '*': + at_deleteSMSTextFromMem_wnc('1'); + at_deleteSMSTextFromMem_wnc('2'); + at_deleteSMSTextFromMem_wnc('3'); + return (true); // WNC may error if slot empty, just ignore! + + case '1': + case '2': + case '3': + if (true == at_deleteSMSTextFromMem_wnc(msgIdx)) + return (true); + else { + dbgPuts(err); + return (false); + } + + default: + dbgPuts(err); + return (false); + } +} + +bool WncController::sendSMSTextFromMem(char msgIdx) +{ + const char * err = "deleteSMSTextFromMem: Failed!"; + + switch (msgIdx) + { + case '*': + at_sendSMStextMem_wnc('1'); + at_sendSMStextMem_wnc('2'); + at_sendSMStextMem_wnc('3'); + return (true); // WNC may error if slot is empty, just ignore! + + case '1': + case '2': + case '3': + if (at_sendSMStextMem_wnc(msgIdx) == true) + return (true); + else { + dbgPuts(err); + return (false); + } + + default: + dbgPuts(err); + return (false); + } +} + +bool WncController::at_deleteSMSTextFromMem_wnc(char n) +{ + string cmdStr, respStr; + // Message is stored in WNC, now send it! + cmdStr = "AT+CMGD="; + cmdStr += n; + cmdStr += "\r\n"; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false); + AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr); + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + return (r == WNC_AT_CMD_OK); +} + +bool WncController::at_sendSMStextMem_wnc(char n) +{ + string cmdStr, respStr; + // Message is stored in WNC, now send it! + cmdStr = "AT+CMSS="; + cmdStr += n; + cmdStr += "\r\n"; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str(), false); + AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), m_sCmdTimeoutMs, &respStr); + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + return (r == WNC_AT_CMD_OK); +} + +bool WncController::at_sendSMStext_wnc(const char * const phoneNum, const char * const text) +{ + string respStr; + string * pRespStr; + size_t l = strlen(text); + + if (l <= MAX_WNC_SMS_LENGTH) + { + // Check to see if the SMS service is available + checkCellLink(); + if (m_sReadyForSMS == true) { + at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs); + string cmdStr("AT+CMGS=\""); + cmdStr += phoneNum; + cmdStr += "\""; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); + cmdStr += "\x0d"; // x0d = <ENTER> + // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet! + // And we want a delay before sending the actual text part of the string! + mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false); // False turns off auto-addition of CR+LF (the WNC wants nothing here) + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) { + // Part 2 of the text, this is the actual text part: + cmdStr = text; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); + cmdStr += "\x1A"; // <CTRL>-Z is what tells the WNC the message is complete to send! + AtCmdErr_e r = mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr); + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + if (respStr.size() == 0) + return (false); + else + return (r == WNC_AT_CMD_OK); + } + } + } + + return (false); +} + +bool WncController::saveSMSText(const char * const phoneNum, const char * const text, char * msgIdx) +{ + if (at_saveSMStext_wnc(phoneNum, text, msgIdx) == true) + return (true); + else { + dbgPuts("saveSMSTextToMem: failed!\r\n"); + return (false); + } +} + +bool WncController::at_saveSMStext_wnc(const char * const phoneNum, const char * const text, char * msgIdx) +{ + string respStr; + size_t l = strlen(text); + + if (l <= MAX_WNC_SMS_LENGTH) + { + // Check to see if the SMS service is available + checkCellLink(); + if (m_sReadyForSMS == true) { + string cmdStr("AT+CMGW=\""); + cmdStr += phoneNum; + cmdStr += "\""; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); + cmdStr += "\x0d"; // x0d = <ENTER> + // Send raw command with short timeout (the timeout will fail cause the WNC is not supposed to reply yet! + // And we want a delay before sending the actual text part of the string! + mdmSendAtCmdRsp(cmdStr.c_str(), 300, &respStr, false); // False turns off auto-addition of CR+LF (the WNC wants nothing here) + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + if ((respStr.size() > 0) && (respStr.find("ERROR") == string::npos)) { + // Part 2 of the text, this is the actual text part: + cmdStr = text; + dbgPuts("TX: ", false); dbgPutsNoTime(cmdStr.c_str()); + cmdStr += "\x1A"; // <CTRL>-Z is what tells the WNC the message is complete to save! + mdmSendAtCmdRsp(cmdStr.c_str(), 10000, &respStr); + dbgPuts("RX: ", false); dbgPutsNoTime(respStr.c_str()); + if (respStr.size() > 0) { + // respStr will have the SMS index + size_t pos1 = respStr.find("+CMGW: "); + size_t pos2 = respStr.rfind("OK"); + if (pos1 != string::npos && pos2 != string::npos) { + *msgIdx = *string(respStr.substr(pos1+7, 1)).c_str(); + return (true); + } + else { + *msgIdx = '!'; + } + } + } + } + } + + return (false); +} + +bool WncController::at_readSMSlog_wnc(string ** log) +{ + return (at_send_wnc_cmd("AT+CMGL", log, m_sCmdTimeoutMs) == WNC_AT_CMD_OK); +} + +size_t WncController::at_readSMStext_wnc(const char n, const char ** log) +{ + static string smsReadTxtStr; + string * pRespStr; + string cmdStr; + + smsReadTxtStr.erase(); + cmdStr = "AT+CMGR"; + cmdStr += '1'; + if (at_send_wnc_cmd("AT+CMGR", &pRespStr, m_sCmdTimeoutMs) == WNC_AT_CMD_OK) + *log = pRespStr->c_str(); + else + *log = "\0"; + + return (pRespStr->size()); +} + +bool WncController::at_at_wnc(void) +{ + string * pRespStr; + return (WNC_AT_CMD_OK == at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS)); // Heartbeat? +} + +bool WncController::at_init_wnc(bool hardReset) +{ + string * pRespStr; + AtCmdErr_e cmdRes; + + if (hardReset == true) + dbgPuts("Hard Soft Reset!"); + + dbgPuts("Start AT init of WNC:"); + + // Kick it twice to perhaps remove cued responses from an incomplete + // power cycle. + at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); + at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); + + // Dump the firmware revision on the debug log: + at_send_wnc_cmd("AT+GMR", &pRespStr, m_sCmdTimeoutMs); + + m_FirmwareRevision = pRespStr->c_str(); + + // Quick commands below do not need to check cellular connectivity + at_send_wnc_cmd("ATE0", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); // Echo Off + at_send_wnc_cmd("AT+CMEE=2", &pRespStr, m_sCmdTimeoutMs); // 2 - verbose error, 1 - numeric error, 0 - just ERROR + + // Setup 3 memory slots in the WNC SIM for SMS usage. + at_send_wnc_cmd("AT+CMGF=1", &pRespStr, m_sCmdTimeoutMs); + at_send_wnc_cmd("AT+CPMS=\"SM\",\"SM\",\"SM\"", &pRespStr, m_sCmdTimeoutMs); + + cmdRes = at_send_wnc_cmd("AT", &pRespStr, WNC_QUICK_CMD_TIMEOUT_MS); // Heartbeat? + + // If the simple commands are not working, no chance of more complex. + // I have seen re-trying commands make it worse. + if (cmdRes != WNC_AT_CMD_OK) + return (false); + + // Disable unsolicited RRCSTATE responses. These are supposed to be off + // by default but have been found to be active. + // This problem introduced in: NQ_MPSS_IMA3_v10.58.174043 LTE-M firmware + cmdRes = at_send_wnc_cmd("AT%NOTIFYEV=\"ALL\",0", &pRespStr, m_sCmdTimeoutMs); + if (cmdRes != WNC_AT_CMD_OK) + return (false); + + cmdRes = at_send_wnc_cmd("AT@INTERNET=1", &pRespStr, m_sCmdTimeoutMs); + if (cmdRes != WNC_AT_CMD_OK) + return (false); + + cmdRes = at_send_wnc_cmd("AT@SOCKDIAL=1", &pRespStr, m_sCmdTimeoutMs); + if (cmdRes != WNC_AT_CMD_OK) + return (false); + + dbgPuts("SUCCESS: AT init of WNC!"); + + return (true); +} + +int16_t WncController::at_sockopen_wnc(const char * const ip, uint16_t port, uint16_t numSock, bool tcp, uint16_t timeOutSec) +{ + string * pRespStr; + string cmd_str("AT@SOCKCREAT="); + AtCmdErr_e res; + + if (tcp) cmd_str += "1"; // TCP + else cmd_str += "2"; // else UDP + + cmd_str += ",0"; + res = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + if (res == WNC_AT_CMD_OK && pRespStr->size() > 0) + { + size_t pos1 = pRespStr->find("T:"); + size_t pos2 = pRespStr->rfind("OK"); + if ((pos1 != string::npos) && (pos2 != string::npos)) { + size_t numLen = pos2 - (pos1 + 2); + string sockStr = pRespStr->substr(pos1 + 2, numLen); + cmd_str = "AT@SOCKCONN="; + cmd_str += sockStr; + cmd_str += ",\""; + cmd_str += ip; + cmd_str += "\","; + cmd_str += _to_string(port); + cmd_str += ","; + if (timeOutSec < 30) + timeOutSec = 30; + else if (timeOutSec > 360) + timeOutSec = 360; + cmd_str += _to_string(timeOutSec); + res = sendWncCmd(cmd_str.c_str(), &pRespStr, 1000 * timeOutSec + 1000); + if (m_sMoreDebugEnabled) { + at_send_wnc_cmd("AT@SOCKCREAT?", &pRespStr, m_sCmdTimeoutMs); + at_send_wnc_cmd("AT@SOCKCONN?", &pRespStr, m_sCmdTimeoutMs); + } + return (strtol(sockStr.c_str(), NULL, 10)); + } + else { + dbgPuts("Invalid sockcreat response!"); + return (0); + } + } + else + return (0); +} + +bool WncController::at_sockclose_wnc(uint16_t numSock) +{ + string * pRespStr; + string cmd_str("AT@SOCKCLOSE="); + + cmd_str += _to_string(numSock); + + // Don't check the cell status to close the socket + AtCmdErr_e res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + + if ((res != WNC_AT_CMD_TIMEOUT) && (res != WNC_AT_CMD_OK)) { + for (unsigned i = 0; i < WNC_SOCK_CLOSE_RETRY_CNT; i++) { + res = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + if ((res == WNC_AT_CMD_TIMEOUT) || (res == WNC_AT_CMD_OK)) + break; + } + } + + return (res == WNC_AT_CMD_OK); +} + +bool WncController::at_dnsresolve_wnc(const char * s, string * ipStr) +{ + string * pRespStr; + string str(s); + AtCmdErr_e r; + + ipStr->erase(); // Clear out string until resolved! + str = "AT@DNSRESVDON=\"" + str; + str += "\""; + r = sendWncCmd(str.c_str(), &pRespStr, WNC_DNS_RESOLVE_WAIT_MS); + if (r == WNC_AT_CMD_OK && pRespStr->size() > 0) { + size_t pos_start = pRespStr->find("ON:\""); + size_t pos_end = pRespStr->find("\"", (pos_start + 4)); + if ((pos_start != string::npos) && (pos_end != string::npos)) { + pos_start += 4; + pos_end -= 1; + + if (pos_end > pos_start) { + // Make a copy for use later (the source string is re-used) + *ipStr = pRespStr->substr(pos_start, pos_end - pos_start + 1); + return (true); + } + } + } + + *ipStr = INVALID_IP_STR; + + return (false); +} + +bool WncController::waitForPowerOnModemToRespond(uint8_t timeoutSecs) +{ + // Now, give the modem x seconds to start responding by + // sending simple 'AT' commands to modem once per second. + if (timeoutSecs > 0) { + do { + timeoutSecs--; + dbgPutsNoTime("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false); + dbgPutsNoTime(" ", false); + AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr); + if (rc == WNC_AT_CMD_OK) { + dbgPutsNoTime(""); // CR LF + return true; //timer.read(); + } + waitMs(500); + } + while (timeoutSecs > 0); + dbgPutsNoTime(""); // CR LF + } + + return (false); +} + +WncController::AtCmdErr_e WncController::at_sockwrite_wnc(const uint8_t * s, uint16_t n, uint16_t numSock, bool isTcp) +{ + AtCmdErr_e result; + + if ((n > 0) && (n <= MAX_WNC_WRITE_BYTES)) { + string * pRespStr; + const char * num2str; + string cmd_str; + + if (isTcp == true) + cmd_str="AT@SOCKWRITE="; + else + cmd_str="AT@SOCKWRITE="; // "AT@SOCKSEND="; + + cmd_str += _to_string(numSock); + cmd_str += ","; + cmd_str += _to_string(n); + cmd_str += ",\""; + while(n > 0) { + n--; + num2str = _to_hex_string(*s++); + // Always 2-digit ascii hex: + if (num2str[1] == '\0') + cmd_str += '0'; + cmd_str += num2str; + } + cmd_str += "\""; + result = sendWncCmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + } + else { + dbgPuts("sockwrite Err, string len bad!"); + result = WNC_AT_CMD_ERR; + } + + return (result); +} + +WncController::AtCmdErr_e WncController::at_sockread_wnc(string * pS, uint16_t numSock, bool isTcp) +{ + AtCmdErr_e result = WNC_AT_CMD_OK; + + string * pRespStr; + string cmd_str; + size_t pos_start=0, pos_end=0; + int i; + + pS->erase(); // Start with a fresh string + + if (isTcp == true) + cmd_str="AT@SOCKREAD="; + else + cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV="; + + cmd_str += _to_string(numSock); + cmd_str += ","; + cmd_str += _to_string(MAX_WNC_READ_BYTES); + + // Experimental: read should not need to check cell net status + result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + if (result == WNC_AT_CMD_OK) { + if (pRespStr->size() > 0) { + pos_start = pRespStr->find("\""); + pos_end = pRespStr->rfind("\""); + // Make sure search finds what it's looking for! + if (pos_start != string::npos && pos_end != string::npos) { + pos_start++; + i = pos_end - pos_start; // Num hex chars, 2 per byte + } + else + i = 0; + } + else + i = 0; + + if ((i < 0) || ((i % 2) == 1)) + dbgPuts("Invalid READ string!"); + + if (i > 2*MAX_WNC_READ_BYTES) { + i = 2*MAX_WNC_READ_BYTES; + dbgPuts("DANGER WNC read data does not match length!"); + } + + // If data, convert the hex string into byte values + while (i > 0) { + i -= 2; + *pS += (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16); + pos_start += 2; + } + } + + return (result); +} + +WncController::AtCmdErr_e WncController::at_sockread_wnc(uint8_t * pS, uint16_t * numRead, uint16_t n, uint16_t numSock, bool isTcp) +{ + AtCmdErr_e result = WNC_AT_CMD_OK; + *numRead = 0; + + if ((n > 0) && (n <= MAX_WNC_READ_BYTES)) { + string * pRespStr; + string cmd_str; + size_t pos_start=0, pos_end=0; + int i; + + if (isTcp == true) + cmd_str="AT@SOCKREAD="; + else + cmd_str="AT@SOCKREAD="; // "AT@SOCKRECV="; + + cmd_str += _to_string(numSock); + cmd_str += ","; + cmd_str += _to_string(n); + + // Experimental: read should not need to check cell net status + result = at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, m_sCmdTimeoutMs); + if (result == WNC_AT_CMD_OK) { + if (pRespStr->size() > 0) { + pos_start = pRespStr->find("\""); + pos_end = pRespStr->rfind("\""); + // Make sure search finds what it's looking for! + if (pos_start != string::npos && pos_end != string::npos) { + pos_start++; + i = pos_end - pos_start; // Num hex chars, 2 per byte + } + else + i = 0; + } + else + i = 0; + + if ((i < 0) || ((i % 2) == 1)) + dbgPuts("Invalid READ string!"); + + if (i > 2*n) { + // Bound the ill formated WNC read string! + i = 2*n; + dbgPuts("TRUNCATING read data!"); + } + + // If data, convert the hex string into byte values + i /= 2; + *numRead = i; + while (i > 0) { + i--; + *pS++ = (uint8_t)strtol(pRespStr->substr(pos_start, 2).c_str(), NULL, 16); + pos_start += 2; + } + } + } + else { + dbgPuts("sockread Err, to many to read!"); + result = WNC_AT_CMD_ERR; + } + + return (result); +} + +bool WncController::at_reinitialize_mdm(void) +{ + // Atempt to re-register +// string * pRespStr; +// dbgPuts("Force re-register!"); +// at_send_wnc_cmd("AT+CFUN=0,0", &pRespStr, m_sCmdTimeoutMs); +// waitMs(31000); +// at_send_wnc_cmd("AT+CFUN=1,0", &pRespStr, m_sCmdTimeoutMs); +// waitMs(31000); + + // Initialize the modem + dbgPuts("Modem RE-initializing with SOFT Reset..."); + + string * pRespStr; + at_send_wnc_cmd("AT@DMREBOOT", &pRespStr, m_sCmdTimeoutMs); + waitMs(5000); + + // Now, give the modem time to start responding by + // sending simple 'AT' commands to the modem once per second. + int timeoutSecs = WNC_REINIT_MAX_TIME_MS; + do { + dbgPuts("\rWaiting ", false); dbgPutsNoTime(_to_string(timeoutSecs), false); + AtCmdErr_e rc = mdmSendAtCmdRsp("AT", 500, &m_sWncStr); + if (rc == WNC_AT_CMD_OK) { + dbgPutsNoTime(""); // CR LF + break; + } + waitMs(500); + timeoutSecs--; + } + while (timeoutSecs > 0); + + if (timeoutSecs <= 0) + dbgPuts("\r\nModem RE-init FAILED!"); + else + dbgPuts("\r\nModem RE-init complete!"); + + return (timeoutSecs > 0); +} + +WncController::AtCmdErr_e WncController::mdmSendAtCmdRsp(const char *cmd, int timeout_ms, string * rsp, bool crLf) +{ + rsp->erase(); // Clean up from possible prior cmd response + + // Don't bother the WNC if user hasn't turned it on. + if (m_sState == WNC_OFF) + return (WNC_AT_CMD_WNC_NOT_ON); + + size_t n = strlen(cmd); + + // Wait per WNC advise + waitMs(WNC_WAIT_FOR_AT_CMD_MS); + + if (cmd && n > 0) { + sendCmd(cmd, crLf); +// sendCmd(cmd, n, 1000, crLf); // 3rd arg is micro seconds between chars sent + } + + startTimerA(); + while (getTimerTicksA_mS() < timeout_ms) { + n = mdmGetline(rsp, timeout_ms - getTimerTicksA_mS()); + + if (n == 0) + continue; + + if (rsp->rfind("OK") != string::npos) { + stopTimerA(); + return (WNC_AT_CMD_OK); + } + + if (rsp->rfind("+CME ERROR") != string::npos) { + stopTimerA(); + return (WNC_AT_CMD_ERRCME); + } + + if (rsp->rfind("@EXTERR") != string::npos) { + stopTimerA(); + return (WNC_AT_CMD_ERREXT); + } + + if (rsp->rfind("ERROR") != string::npos) { + stopTimerA(); + return (WNC_AT_CMD_ERR); + } + } + stopTimerA(); + + return (WNC_AT_CMD_TIMEOUT); +} + +bool WncController::at_setapn_wnc(const char * const apnStr) +{ + string * pRespStr; + + string cmd_str("AT%PDNSET=1,"); + cmd_str += apnStr; + cmd_str += ",IP"; + if (WNC_AT_CMD_OK == at_send_wnc_cmd(cmd_str.c_str(), &pRespStr, WNC_APNSET_TIMEOUT_MS)) // Set APN, cmd seems to take a little longer sometimes + return (true); + else + return (false); +} + +bool WncController::at_getrssiber_wnc(int16_t * dBm, int16_t * ber) +{ + string * pRespStr; + AtCmdErr_e cmdRes; + cmdRes = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs); // Check RSSI,BER + if (cmdRes != WNC_AT_CMD_OK) + return (false); + + if (pRespStr->size() == 0) { + dbgPuts("Strange RSSI result!"); + return (false); + } + else { + size_t pos1 = pRespStr->find("SQ:"); + size_t pos2 = pRespStr->rfind(","); + // Sanity check + if ((pos1 != string::npos) && (pos2 != string::npos) && (pos2 > pos1)) { + string subStr = pRespStr->substr(pos1 + 4, pos2 - pos1 ); + int rawRssi = atoi(subStr.c_str()); + + // Convert WNC RSSI into dBm range: + // 0 - -113 dBm + // 1 - -111 dBm + // 2..30 - -109 to -53 dBm + // 31 - -51dBm or > + // 99 - not known or not detectable + if (rawRssi == 99) + *dBm = -199; + else if (rawRssi == 0) + *dBm = -113; + else if (rawRssi == 1) + *dBm = -111; + else if (rawRssi == 31) + *dBm = -51; + else if (rawRssi >= 2 && rawRssi <= 30) + *dBm = -113 + 2 * rawRssi; + else { + dbgPuts("Invalid RSSI!"); + return (false); + } + // Parse out BER: 0..7 as RXQUAL values in the table 3GPP TS 45.008 subclause 8.2.4 + // 99 - unknown or undetectable + subStr = pRespStr->substr(pos2 + 1, pRespStr->length() - (pos2 + 1)); + *ber = atoi(subStr.c_str()); + } + else { + dbgPuts("Strange RSSI result2!"); + return (false); + } + } + + return (true); +} + +bool WncController::checkCellLink(void) +{ + string * pRespStr; + size_t pos; + int regSts; + int cmdRes1, cmdRes2; + + if (m_sState == WNC_OFF) + return (false); + + m_sState = WNC_ON_NO_CELL_LINK; + + if (m_sMoreDebugEnabled) + dbgPuts("<-------- Begin Cell Status ------------"); + + cmdRes1 = at_send_wnc_cmd("AT+CSQ", &pRespStr, m_sCmdTimeoutMs); // Check RSSI,BER + + // If no response, don't bother with more commands + if (cmdRes1 != WNC_AT_CMD_TIMEOUT) + cmdRes2 = at_send_wnc_cmd("AT+CPIN?", &pRespStr, m_sCmdTimeoutMs); // Check if SIM locked + else { + if (m_sMoreDebugEnabled) + dbgPuts("------------ WNC No Response! --------->"); + + return (false); + } + + if ((cmdRes1 != WNC_AT_CMD_OK) || (cmdRes2 != WNC_AT_CMD_OK) || (pRespStr->size() == 0)) + { + if (m_sMoreDebugEnabled) + { + if ((cmdRes1 == WNC_AT_CMD_TIMEOUT) || (cmdRes2 == WNC_AT_CMD_TIMEOUT)) + dbgPuts("------------ WNC No Response! --------->"); + else + dbgPuts("------------ WNC Cmd Error! ----------->"); + } + + // If by a miracle it responds to the 2nd after the 1st, keep going + if ((cmdRes2 == WNC_AT_CMD_TIMEOUT) || (pRespStr->size() == 0)) + return (false); + } + + // If SIM Card not ready don't bother with commands! + if (pRespStr->find("CPIN: READY") == string::npos) + { + if (m_sMoreDebugEnabled) + dbgPuts("------------ WNC SIM Problem! --------->"); + + return (false); + } + + // SIM card OK, now check for signal and cellular network registration + cmdRes1 = at_send_wnc_cmd("AT+CREG?", &pRespStr, m_sCmdTimeoutMs); // Check if registered on network + if (cmdRes1 != WNC_AT_CMD_OK || pRespStr->size() == 0) + { + if (m_sMoreDebugEnabled) + dbgPuts("------------ WNC +CREG? Fail! --------->"); + + return (false); + } + else + { + pos = pRespStr->find("CREG: "); + if (pos != string::npos) + { + // The registration is the 2nd arg in the comma separated list + *pRespStr = pRespStr->substr(pos+8, 1); + regSts = atoi(pRespStr->c_str()); + switch (regSts) { + case 1: + case 5: + case 6: + case 7: + m_sReadyForSMS = true; + break; + default: + m_sReadyForSMS = false; + dbgPuts("SMS Service Down!"); + } + + // 1 - registered home, 5 - registered roaming + if ((regSts != 1) && (regSts != 5)) + { + if (m_sMoreDebugEnabled) + dbgPuts("------ WNC Cell Link Down for Data! --->"); + + return (false); + } + } + + if (m_sMoreDebugEnabled) + dbgPuts("------------ WNC Ready ---------------->"); + } + + // If we made it this far and the WNC did respond, keep the ON state + if (m_sState != WNC_NO_RESPONSE) + m_sState = WNC_ON; + + return (true); +} + +int WncController::dbgPutsNoTime(const char * s, bool crlf) +{ + if (m_sDebugEnabled == true) { + int r = dbgWriteChars(s); + if (crlf == true) + return (dbgWriteChars("\r\n")); + else + return (r); + } + else + return 0; +}; + +int WncController::dbgPuts(const char * s, bool crlf) +{ + dbgPutsNoTime("[*] ", false); + dbgPutsNoTime(_to_string(getLogTimerTicks()), false); + dbgPutsNoTime(" ", false); + + int r = dbgPutsNoTime(s, false); + + if (crlf == true) + return (dbgPutsNoTime("", true)); + else + return (r); +}; + +void WncController::sendCmd(const char * cmd, bool crLf) +{ + puts(cmd); + if (crLf == true) + puts("\r\n"); +} + +void WncController::sendCmd(const char * cmd, unsigned n, unsigned wait_uS, bool crLf) +{ + while (n--) { + putc(*cmd++); + waitUs(wait_uS); + }; + if (crLf == true) { + putc('\r'); + waitUs(wait_uS); + putc('\n'); + waitUs(wait_uS); + } +} + +}; // End namespace WncController_fk +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/WNC14A2AInterface/WncControllerK64F/WncController/WncController.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,636 @@ +/** + Copyright (c) 2016 Fred Kellerman + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + @file WncController.h + @purpose Controls WNC Cellular Modem + @version 1.0 + @date July 2016 + @author Fred Kellerman + + Notes: This code originates from the following mbed repository: + + https://developer.mbed.org/teams/Avnet/code/WncControllerLibrary/ +*/ + + +#ifndef __WNCCONTROLLER_H_ +#define __WNCCONTROLLER_H_ + +#include <string> +#include <stdint.h> + +namespace WncController_fk { + +using namespace std; + +/** @defgroup API The WncControllerLibrary API */ +/** @defgroup MISC Misc WncControllerLibrary functions */ +/** @defgroup INTERNALS WncControllerLibrary Internals */ + +static const uint8_t MAX_LEN_IP_STR = 16; // Length includes room for the extra NULL + +/** \brief Contains info fields for the WNC Internet Attributes */ +struct WncIpStats +{ + string wncMAC; + char ip[MAX_LEN_IP_STR]; + char mask[MAX_LEN_IP_STR]; + char gateway[MAX_LEN_IP_STR]; + char dnsPrimary[MAX_LEN_IP_STR]; + char dnsSecondary[MAX_LEN_IP_STR]; +}; + + +/** + * @author Fred Kellerman + * @see API + * + * <b>WncController</b> This mbed C++ class is for controlling the WNC + * Cellular modem via the serial AT command interface. This was + * developed with respect to version 1.3 of the WNC authored + * unpublished spec. This class is only designed to have 1 instantiation, + * it is also not multi-thread safe. There are no OS specific + * entities being used, there are pure virtual methods that an + * inheriting class must fulfill. That inheriting class will have + * OS and platform specific entities. See WncControllerK64F for an + * example for the NXP K64F Freedom board. + */ +class WncController +{ +public: + + static const unsigned MAX_NUM_WNC_SOCKETS = 5; // Max number of simultaneous sockets that the WNC supports + static const unsigned MAX_POWERUP_TIMEOUT = 60; // How long the powerUp method will try to turn on the WNC Shield + // (this is the default if the user does not over-ride on power-up + + /** Tracks mode of the WNC Shield hardware */ + enum WncState_e { + WNC_OFF = 0, + WNC_ON, // This is intended to mean all systems go, including cell link up but socket may not be open + WNC_ON_NO_CELL_LINK, + WNC_NO_RESPONSE + }; + + /** + * + * Constructor for WncController class, sets up internals. + * @ingroup API + * @return none. + */ + WncController(void); + virtual ~WncController()=0; + + /** + * + * Used internally but also make public for a user of the Class to + * interrogate state as well. + * @ingroup API + * @return the current state of the Wnc hardware. + */ + WncState_e getWncStatus(void); + + /** + * + * Allows a user to set the WNC modem to use the given Cellular APN + * @ingroup API + * @param apnStr - a null terminated c-string + * @return true if the APN set was succesful, else false + */ + bool setApnName(const char * const apnStr); + + /** + * + * Queries the WNC modem for the current RX RSSI in units of coded dBm + * @ingroup API + * @return 0 â -113 dBm or less + * 1 â -111 dBm + * 2...30 â -109 dBm to â53 dBm + * 31 â -51 dBm or greater + * 99 â not known or not detectable + */ + int16_t getDbmRssi(void); + + /** + * + * Queries the WNC modem for the current Bit Error Rate + * @ingroup API + * @return 0...7 â as RXQUAL values in the table in 3GPP TS 45.008 + * subclause 8.2.4 + * 99 â not known or not detectable + */ + int16_t get3gBer(void); + + /** + * + * Powers up the WNC modem + * @ingroup API + * @param apn - the apn c-string to set the WNC modem to use + * @param powerUpTimeoutSecs - the amount of time to wait for the WNC modem to turn on + * @return true if powerup was a success, else false. + */ + bool powerWncOn(const char * const apn, uint8_t powerUpTimeoutSecs = MAX_POWERUP_TIMEOUT); + + /** + * + * Returns the NAT Self, gateway, masks and dns IP + * @ingroup API + * @param s - a pointer to a struct that will contain the IP info. + * @return true if success, else false. + */ + bool getWncNetworkingStats(WncIpStats * s); + + /** + * + * Takes a text URL and converts it internally to an IP address for the + * socket number given. + * @ingroup API + * @param numSock - The number of the socket to lookup the IP address for. + * @param url - a c-string text URL + * @return true if success, else false. + */ + bool resolveUrl(uint16_t numSock, const char * url); + + /** + * + * If you know the IP address you can set the socket up to use it rather + * than using a text URL. + * @ingroup API + * @param numSock - The number of the socket to use the IP address for. + * @param ipStr - a c-string text IP addrese like: 192.168.0.1 + * @return true if success, else false. + */ + bool setIpAddr(uint16_t numSock, const char * ipStr); + + /** + * + * Opens a socket for the given number, port and IP protocol. Before + * using open, you must use either resolveUrl() or setIpAddr(). + * @ingroup API + * @param numSock - The number of the socket to open. + * @param port - the IP port to open + * @param tcp - set true for TCP, false for UDP + * @param timeoutSec - the amount of time in seconds to wait for the open to complete + * @return true if success, else false. + */ + bool openSocket(uint16_t numSock, uint16_t port, bool tcp, uint16_t timeOutSec = 30); + + /** + * + * Opens a socket for the given text URL, number, port and IP protocol. + * @ingroup API + * @param numSock - The number of the socket to open. + * @param url - a c-string text URL, the one to open a socket for. + * @param port - the IP port to open. + * @param tcp - set true for TCP, false for UDP. + * @param timeoutSec - the amount of time in seconds to wait for the open to complete. + * @return true if success, else false. + */ + bool openSocketUrl(uint16_t numSock, const char * url, uint16_t port, bool tcp, uint16_t timeOutSec = 30); + + /** + * + * Opens a socket for the given text IP address, number, port and IP protocol. + * @ingroup API + * @param numSock - The number of the socket to open. + * @param ipAddr - a c-string text IP address like: "192.168.0.1". + * @param port - the IP port to open. + * @param tcp - set true for TCP, false for UDP. + * @param timeoutSec - the amount of time in seconds to wait for the open to complete. + * @return true if success, else false. + */ + bool openSocketIpAddr(uint16_t numSock, const char * ipAddr, uint16_t port, bool tcp, uint16_t timeOutSec = 30); + + + /** + * + * Write data bytes to a Socket, the Socket must already be open. + * @ingroup API + * @param numSock - The number of the socket to open. + * @parma s - an array of bytes to write to the socket. + * @param n - the number of bytes to write. + * @return true if success, else false. + */ + bool write(uint16_t numSock, const uint8_t * s, uint32_t n); + + /** + * + * Poll to read available data bytes from an already open Socket. This method + * will retry reads to what setReadRetries() sets it to and the delay in between + * retries that is set with setReadRetryWait() + * @ingroup API + * @param numSock - The number of the socket to open. + * @parma readBuf - a pointer to where read will put the data. + * @param maxReadBufLen - The number of bytes readBuf has room for. + * @return the number of bytes actually read into readBuf. 0 is a valid value if no data is available. + */ + size_t read(uint16_t numSock, uint8_t * readBuf, uint32_t maxReadBufLen); + + /** + * + * Poll to read available data bytes from an already open Socket. This method + * will retry reads to what setReadRetries() sets it to and the delay in between + * retries that is set with setReadRetryWait() + * @ingroup API + * @param numSock - The number of the socket to open. + * @parma readBuf - a pointer to pointer that will be set to point to an internal byte buffer that contains any read data. + * @return the number of bytes actually read into the pointer that readBuf points to. 0 is a valid value if no data is available. + */ + size_t read(uint16_t numSock, const uint8_t ** readBuf); + + /** + * + * Set the number of retries that the read methods will use. If a read returns 0 data this setting will have the read + * re-read to see if new data is available. + * @ingroup API + * @param numSock - The number of the socket to open. + * @parma retries - the number of retries to perform. + * @return none. + */ + void setReadRetries(uint16_t numSock, uint16_t retries); + + /** + * + * Set the time between retires that the read methods will use. If a read returns 0 data this setting will have the read + * re-read and use this amount of delay in between the re-reads. + * @ingroup API + * @param numSock - The number of the socket to open. + * @parma waitMs - the amount of time in mS to wait between retries. + * @return none. + */ + void setReadRetryWait(uint16_t numSock, uint16_t waitMs); + + /** + * + * Closes an already open Socket. + * @ingroup API + * @param numSock - The number of the socket to open. + * @return true if success else false. + */ + bool closeSocket(uint16_t numSock); + + /** + * + * Sets the amount of time to wait between the raw AT commands that are sent to the WNC modem. + * Generally you don't want to use this but it is here just in case. + * @ingroup API + * @param toMs - num mS to wait between the AT cmds. + * @return none. + */ + void setWncCmdTimeout(uint16_t toMs); + + /** + * + * Gets the IP address of the given socket number. + * @ingroup API + * @param numSock - The number of the socket to open. + * @param myIpAddr - a c-string that contains the socket's IP address. + * @return true if success else false. + */ + bool getIpAddr(uint16_t numSock, char myIpAddr[MAX_LEN_IP_STR]); + + /** + * + * Enables debug output from this class. + * @ingroup API + * @param on - true enables debug output, false disables + * @param moreDebugOn - true enables verbose debug, false truncates debug output. + * @return none. + */ + void enableDebug(bool on, bool moreDebugOn); + + /** + * + * Reports the WNC Firmware Revision info. + * @ingroup API + * @param none. + * @return char * + */ + const char* getFirmRev(void); + + /////////////////////////////////////////// + // SMS messaging + /////////////////////////////////////////// + + static const uint16_t MAX_WNC_SMS_MSG_SLOTS = 3; // How many SMS messages the WNC can store and receive at a time. + static const uint16_t MAX_WNC_SMS_LENGTH = 160; // The maximum length of a 7-bit SMS message the WNC can send and receive. + + /** Struct for SMS messages */ + struct WncSmsInfo + { + // Content + char idx; + string number; + string date; + string time; + string msg; + + // Attributes + bool incoming; + bool unsent; + bool unread; + bool pduMode; + bool msgReceipt; + }; + + /** Struct to contain a list of SMS message structs */ + struct WncSmsList + { + uint8_t msgCount; + WncSmsInfo e[MAX_WNC_SMS_MSG_SLOTS]; + }; + + /** + * + * Sends an SMS text message to someone. + * @ingroup API + * @param phoneNum - c-string 15 digit MSISDN number or ATT Jasper number (standard phone number not supported because ATT IoT SMS does not support it). + * @param text - the c-string text to send to someone. + * @return true if success else false. + */ + bool sendSMSText(const char * const phoneNum, const char * const text); + + /** + * + * Incoming messages are stored in a log in the WNC modem, this will read that + * log. + * @ingroup API + * @param log - the log contents if reading it was successful. + * @return true if success else false. + */ + bool readSMSLog(struct WncSmsList * log); + + /** + * + * Incoming messages are stored in a log in the WNC modem, this will read out + * messages that are unread and also then mark them read. + * @ingroup API + * @param w - a list of SMS messages that unread messages will be put into. + * @param deleteRead - if a message is read and this is set true the message will be deleted from the WNC modem log. + * If it is false the message will remain in the internal log but be marked as read. + * @return true if success else false. + */ + bool readUnreadSMSText(struct WncSmsList * w, bool deleteRead = true); + + /** + * + * Saves a text message into internal SIM card memory of the WNC modem. + * There are only 3 slots available this is for unread, read and saved. + * @ingroup API + * @param phoneNum - c-string 15 digit MSISDN number or ATT Jasper number (standard phone number not supported because ATT IoT SMS does not support it). + * @param text - the c-string text to send to someone. + * @param msgIdx - the slot position to save the message: '1', '2', '3' + * @return true if success else false. + */ + bool saveSMSText(const char * const phoneNum, const char * const text, char * msgIdx); + + /** + * + * Sends a prior stored a text message from internal SIM card memory of the WNC modem. + * If no messages are stored the behaviour of this method is undefined. + * @ingroup API + * @param msgIdx - the slot position to save the message: '1', '2', '3' + * @return true if success else false. + */ + bool sendSMSTextFromMem(char msgIdx); + + /** + * + * Deletes a prior stored a text message from internal SIM card memory of the WNC modem. + * If no messages are stored the behaviour of this method is undefined. + * @ingroup API + * @param msgIdx - the slot position to save the message: '1', '2', '3' or '*' deletes them all. + * @return true if success else false. + */ + bool deleteSMSTextFromMem(char msgIdx); + + /** + * + * Retreives the SIM card ICCID number. + * @ingroup API + * @param iccid - a pointer to C++ string that contains the retrieved number. + * @return true if success else false. + */ + bool getICCID(string * iccid); + + /** + * + * Converts an ICCID number into a MSISDN number. The ATT SMS system for IoT only allows use of the 15-digit MSISDN number. + * @ingroup API + * @param iccid - the number to convert. + * @param msisdn - points to a C++ string that has the converted number. + * @return true if success else false. + */ + bool convertICCIDtoMSISDN(const string & iccid, string * msisdn); + + /////////////////////////////////////////// + // Neighborhood Cell Info + /////////////////////////////////////////// + + /** + * + * Fetches the signal quality log from the WNC modem. + * @ingroup API + * @param log - a pointer to an internal buffer who's contents contain the signal quality metrics. + * @return The number of chars in the log. + */ + size_t getSignalQuality(const char ** log); + + /** A struct for the WNC modem Date and Time */ + struct WncDateTime + { + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t min; + uint8_t sec; + }; + + /** + * + * Fetches the cell tower's time and date. The time is accurate when read + * but significant delays exist between the time it is read and returned. + * @ingroup API + * @param tod - User supplies a pointer to a tod struct and this method fills it in. + * @return true if success else false. + */ + bool getTimeDate(struct WncDateTime * tod); + + /** + * + * ICMP Pings a URL, the results are only output to the debug log for now! + * @ingroup API + * @param url - a c-string whose URL is to be pinged. + * @return true if success else false. + */ + bool pingUrl(const char * url); + + /** + * + * ICMP Pings an IP, the results are only output to the debug log for now! + * @ingroup API + * @param ip - a c-string whose IP is to be pinged. + * @return true if success else false. + */ + bool pingIp(const char * ip); + + /** + * + * Allows a user to send a raw AT command to the WNC modem. + * @ingroup API + * @param cmd - the c-string cmd to send like: "AT" + * @param resp - a pointer to the c-string cmd's response. + * @param sizeRespBuf - how large the command response buffer is, sets the max response length. + * @param ms_timeout - how long to wait for the WNC to respond to your command. + * @return the number of characters in the response from the WNC modem. + */ + size_t sendCustomCmd(const char * cmd, char * resp, size_t sizeRespBuf, int ms_timeout); + +protected: + + // Debug output methods + int dbgPutsNoTime(const char * s, bool crlf = true); + int dbgPuts(const char * s, bool crlf = true); + const char * _to_string(int64_t value); + const char * _to_hex_string(uint8_t value); + + // Sends commands to WNC via + enum AtCmdErr_e { + WNC_AT_CMD_OK, + WNC_AT_CMD_ERR, + WNC_AT_CMD_ERREXT, + WNC_AT_CMD_ERRCME, + WNC_AT_CMD_INVALID_RESPONSE, + WNC_AT_CMD_TIMEOUT, + WNC_AT_CMD_NO_CELL_LINK, + WNC_AT_CMD_WNC_NOT_ON + }; + + bool waitForPowerOnModemToRespond(uint8_t powerUpTimeoutSecs); + AtCmdErr_e sendWncCmd(const char * const s, string ** r, int ms_timeout); + + // Users must define these functionalities in the inheriting class: + // General I/O and timing: + virtual int putc(char c) = 0; + virtual int puts(const char * s) = 0; + virtual char getc(void) = 0; + virtual int charReady(void) = 0; + virtual int dbgWriteChar(char b) = 0; + virtual int dbgWriteChars(const char *b) = 0; + virtual void waitMs(int t) = 0; + virtual void waitUs(int t) = 0; + virtual bool initWncModem(uint8_t powerUpTimeoutSecs) = 0; + + // Isolate OS timers + virtual int getLogTimerTicks(void) = 0; + virtual void startTimerA(void) = 0; + virtual void stopTimerA(void) = 0; + virtual int getTimerTicksA_mS(void) = 0; + virtual void startTimerB(void) = 0; + virtual void stopTimerB(void) = 0; + virtual int getTimerTicksB_mS(void) = 0; + +private: + + bool softwareInitMdm(void); + bool checkCellLink(void); + AtCmdErr_e mdmSendAtCmdRsp(const char * cmd, int timeout_ms, string * rsp, bool crLf = true); + size_t mdmGetline(string * buff, int timeout_ms); + bool at_at_wnc(void); + bool at_init_wnc(bool hardReset = false); + int16_t at_sockopen_wnc(const char * const ip, uint16_t port, uint16_t numSock, bool tcp, uint16_t timeOutSec); + bool at_sockclose_wnc(uint16_t numSock); + bool at_dnsresolve_wnc(const char * s, string * ipStr); + AtCmdErr_e at_sockwrite_wnc(const uint8_t * s, uint16_t n, uint16_t numSock, bool isTcp); + AtCmdErr_e at_sockread_wnc(uint8_t * pS, uint16_t * numRead, uint16_t n, uint16_t numSock, bool isTcp); + AtCmdErr_e at_sockread_wnc(string * pS, uint16_t numSock, bool isTcp); + bool at_reinitialize_mdm(void); + AtCmdErr_e at_send_wnc_cmd(const char * s, string ** r, int ms_timeout); + bool at_setapn_wnc(const char * const apnStr); + bool at_sendSMStext_wnc(const char * const phoneNum, const char * const text); + bool at_get_wnc_net_stats(WncIpStats * s); + bool at_readSMSlog_wnc(string ** log); + size_t at_readSMStext_wnc(const char ** log); + size_t at_readSMStext_wnc(const char n, const char ** log); + bool at_getrssiber_wnc(int16_t * dBm, int16_t * ber3g); + void closeOpenSocket(uint16_t numSock); + bool sockWrite(const uint8_t * const s, uint16_t n, uint16_t numSock, bool isTcp); + bool at_sendSMStextMem_wnc(char n); + bool at_deleteSMSTextFromMem_wnc(char n); + bool at_saveSMStext_wnc(const char * const phoneNum, const char * const text, char * msgIdx); + size_t at_getSignalQuality_wnc(const char ** log); + bool at_gettimedate_wnc(struct WncDateTime * tod); + bool at_ping_wnc(const char * ip); + bool at_geticcid_wnc(string * iccid); + + // Utility methods + void sendCmd(const char * cmd, bool crLf); + void sendCmd(const char * cmd, unsigned n, unsigned wait_uS, bool crLf); + inline void rx_char_wait(void) { + // waitUs(1000); + } + + // Important constants + static const uint16_t MAX_WNC_READ_BYTES = 1500; // This bounds the largest amount of data that the WNC read from a socket will return + static const uint16_t MAX_WNC_WRITE_BYTES = MAX_WNC_READ_BYTES; // This is the largest amount of data that the WNC can write per sockwrite. + static const uint16_t MAX_LEN_WNC_CMD_RESPONSE = (MAX_WNC_READ_BYTES * 2 + 100); // Max number of text characters in a WNC AT response *2 because bytes are converted into 2 hex-digits +100 for other AT@ chars. + static const uint16_t WNC_AUTO_POLL_MS = 250; // Sets default (may be overriden with method) poll interval (currently not used, future possible feature. + static const uint16_t WNC_CMD_TIMEOUT_MS = 40000; // Sets default (may be overriden) time that the software waits for an AT response from the WNC. + static const uint16_t WNC_QUICK_CMD_TIMEOUT_MS = 2000; // Used for simple commands that should immediately respond such as "AT", cmds that are quicker than WNC_CMD_TIMEOUT_MS. + static const uint16_t WNC_WAIT_FOR_AT_CMD_MS = 0; // Wait this much between multiple in a row AT commands to the WNC. + static const uint16_t WNC_SOFT_INIT_RETRY_COUNT = 10; // How many times the WNC will be tried to revive if it stops responding. + static const uint16_t WNC_DNS_RESOLVE_WAIT_MS = 60000; // How much time to wait for the WNC to respond to a DNS resolve/lookup. + static const uint16_t WNC_TRUNC_DEBUG_LENGTH = 80; // Always make this an even number, how many chars for the debug output before shortening the debug ouput, this is used when moreDebug = false. + static const uint16_t WNC_APNSET_TIMEOUT_MS = 60000; // How long to wait for the WNC to respond to setting the APN string. + static const uint16_t WNC_PING_CMD_TIMEOUT_MS = 60000; // Amount of time to wait for the WNC to respond to AT@PINGREQ (with cmd default params for timeout, does not change WNC cmd's timeout) + static const int WNC_REINIT_MAX_TIME_MS = 60000; // How long to wait for the WNC to reset after it was already up and running after power-up. + static const uint16_t WNC_SOCK_CLOSE_RETRY_CNT = 3; // How many times to try to close the socket if the WNC gives an error. + static const char * const INVALID_IP_STR; // Just a string set to an IP address when DNS resolve fails. + + struct WncSocketInfo_s { + int16_t numWncSock; + bool open; + string myIpAddressStr; + uint16_t myPort; + uint8_t readRetries; + uint16_t readRetryWaitMs; + bool isTcp; + uint16_t timeOutSec; + }; + + static WncSocketInfo_s m_sSock[MAX_NUM_WNC_SOCKETS]; + static const WncSocketInfo_s defaultSockStruct; + static WncState_e m_sState; + static uint16_t m_sCmdTimeoutMs; + static string m_sApnStr; + static string m_sWncStr; + static string m_FirmwareRevision; + static uint8_t m_sPowerUpTimeoutSecs; + static bool m_sDebugEnabled; + static bool m_sMoreDebugEnabled; + static bool m_sCheckNetStatus; + static bool m_sReadyForSMS; +}; + +}; // End namespace WncController_fk + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/WNC14A2AInterface/WncControllerK64F/WncControllerK64F.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,206 @@ +/* + Copyright (c) 2016 Fred Kellerman + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + @file WncControllerK64F.cpp + @purpose Contains K64F and mbed specifics to control the WNC modem using the WncController base class. + @version 1.0 + @date July 2016 + @author Fred Kellerman +*/ + +#include "WncControllerK64F.h" + +using namespace WncControllerK64F_fk; + +WncControllerK64F::WncControllerK64F(struct WncGpioPinListK64F * pPins, WncIO * wnc_uart, WNCDebug * debug_uart) +{ + m_logTimer.start(); // Start the log timer now! + m_pDbgUart = debug_uart; + m_pWncUart = wnc_uart; + m_gpioPinList = *pPins; +} + +bool WncControllerK64F::enterWncTerminalMode(WncIO * pUart, bool echoOn) +{ + if (pUart == NULL) + return (false); // Need a uart! + + string * resp; + AtCmdErr_e r = sendWncCmd("AT", &resp, 500); + if (r == WNC_AT_CMD_TIMEOUT) + return (false); + + pUart->puts("\r\nEntering WNC Terminal Mode - press <CTRL>-Q to exit!\r\n"); + + while (1) { + if (pUart->readable()) { + char c = pUart->getc(); + if (c == '\x11') { + pUart->puts("\r\nExiting WNC Terminal Mode!\r\n"); + // Cleanup in case user doesn't finish command: + sendWncCmd("AT", &resp, 300); + // Above AT may fail but should get WNC back in sync + return (sendWncCmd("AT", &resp, 500) == WNC_AT_CMD_OK); + } + if (echoOn == true) { + pUart->putc(c); + } + m_pWncUart->putc(c); + } + if (m_pWncUart->readable()) + pUart->putc(m_pWncUart->getc()); + } +} + +int WncControllerK64F::putc(char c) +{ + return (m_pWncUart->putc(c)); +} + +int WncControllerK64F::puts(const char * s) +{ + return (m_pWncUart->puts(s)); +} + +char WncControllerK64F::getc(void) +{ + return (m_pWncUart->getc()); +} + +int WncControllerK64F::charReady(void) +{ + return (m_pWncUart->readable()); +} + +int WncControllerK64F::dbgWriteChar(char b) +{ + if (m_pDbgUart != NULL) + return (m_pDbgUart->putc(b)); + else + return (0); +} + +int WncControllerK64F::dbgWriteChars(const char * b) +{ + if (m_pDbgUart != NULL) + return (m_pDbgUart->puts(b)); + else + return (0); +} + +bool WncControllerK64F::initWncModem(uint8_t powerUpTimeoutSecs) +{ + // Hard reset the modem (doesn't go through + // the signal level translator) + *m_gpioPinList.mdm_reset = 0; + + // disable signal level translator (necessary + // for the modem to boot properly). All signals + // except mdm_reset go through the level translator + // and have internal pull-up/down in the module. While + // the level translator is disabled, these pins will + // be in the correct state. + *m_gpioPinList.shield_3v3_1v8_sig_trans_ena = 0; + + // While the level translator is disabled and ouptut pins + // are tristated, make sure the inputs are in the same state + // as the WNC Module pins so that when the level translator is + // enabled, there are no differences. + *m_gpioPinList.mdm_uart2_rx_boot_mode_sel = 1; // UART2_RX should be high + *m_gpioPinList.mdm_power_on = 0; // powr_on should be low + *m_gpioPinList.mdm_wakeup_in = 1; // wake-up should be high + *m_gpioPinList.mdm_uart1_cts = 0; // indicate that it is ok to send + + // Now, wait for the WNC Module to perform its initial boot correctly + waitMs(1000); + + // The WNC module initializes comms at 115200 8N1 so set it up + m_pWncUart->baud(115200); + + //Now, enable the level translator, the input pins should now be the + //same as how the M14A module is driving them with internal pull ups/downs. + //When enabled, there will be no changes in these 4 pins... + *m_gpioPinList.shield_3v3_1v8_sig_trans_ena = 1; + + bool res = waitForPowerOnModemToRespond(powerUpTimeoutSecs); + + // Toggle wakeup to prevent future dropped 'A' of "AT", this was + // suggested by ATT. + if (res == true) { + dbgPuts("\r\nToggling Wakeup..."); + waitMs(20); + *m_gpioPinList.mdm_wakeup_in = 0; + waitMs(2000); + *m_gpioPinList.mdm_wakeup_in = 1; + waitMs(20); + dbgPuts("Toggling complete."); + } + + return (res); +} + +void WncControllerK64F::waitMs(int t) +{ + wait_ms(t); +} + +void WncControllerK64F::waitUs(int t) +{ + wait_ms(t); +} + +int WncControllerK64F::getLogTimerTicks(void) +{ + return (m_logTimer.read_us()); +} + +void WncControllerK64F::startTimerA(void) +{ + m_timerA.start(); + m_timerA.reset(); +} + +void WncControllerK64F::stopTimerA(void) +{ + m_timerA.stop(); +} + +int WncControllerK64F::getTimerTicksA_mS(void) +{ + return (m_timerA.read_ms()); +} + +void WncControllerK64F::startTimerB(void) +{ + m_timerB.start(); + m_timerB.reset(); +} + +void WncControllerK64F::stopTimerB(void) +{ + m_timerB.stop(); +} + +int WncControllerK64F::getTimerTicksB_mS(void) +{ + return (m_timerB.read_ms()); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/WNC14A2AInterface/WncControllerK64F/WncControllerK64F.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,131 @@ +/* + Copyright (c) 2016 Fred Kellerman + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + @file WncControllerK64F.h + @purpose Contains K64F and mbed specifics to control the WNC modem using the WncController base class. + @version 1.0 + @date July 2016 + @author Fred Kellerman +*/ + +#ifndef __WNCCONTROLLERK64F_H_ +#define __WNCCONTROLLERK64F_H_ + +#include <string> +#include <stdint.h> +#include "mbed.h" +#include "WNCDebug.h" +#include "WNCIO.h" +#include "WncController.h" + +namespace WncControllerK64F_fk { + +using namespace WncController_fk; +using namespace std; + +/** List of K64F pins that are used to control and setup the ATT IoT Kit WNC Shield */ +struct WncGpioPinListK64F { + ///////////////////////////////////////////////////// + // NXP GPIO Pins that are used to initialize the WNC Shield + ///////////////////////////////////////////////////// + DigitalOut * mdm_uart2_rx_boot_mode_sel; // on powerup, 0 = boot mode, 1 = normal boot + DigitalOut * mdm_power_on; // 0 = turn modem on, 1 = turn modem off (should be held high for >5 seconds to cycle modem) + DigitalOut * mdm_wakeup_in; // 0 = let modem sleep, 1 = keep modem awake -- Note: pulled high on shield + DigitalOut * mdm_reset; // active high + DigitalOut * shield_3v3_1v8_sig_trans_ena; // 0 = disabled (all signals high impedence, 1 = translation active + DigitalOut * mdm_uart1_cts; +}; + + +/** + * @author Fred Kellerman + * @see API + * + * <b>WncControllerK64F</b> This mbed C++ class is for controlling the WNC + * Cellular modem from the NXP K64F Freedom board. It uses the control code + * from it's base class WncController to handle the WNC Modem AT cmds. This + * class fulfills various pure virtual methods of the base class. The point of + * this class is to have the platform specific code in it thus isolating the + * control code logic from any particular platform or OS. + */ +class WncControllerK64F : public WncController +{ +public: + + /** + * + * Sets up the resources to control the WNC modem shield. + * @ingroup API + * @param pPins - pointer to a list of K64F pins that are used to setup and control the ATT IoT Kit's WNC Shield. + * @param wnc_uart - a pointer to the serial uart that is used to communicate with the WNC modem. + * @param debug_uart - a pointer to a serial uart for the debug output to go out of, if NULL debug will not be output. + */ + WncControllerK64F(struct WncGpioPinListK64F * pPins, WncIO * wnc_uart, WNCDebug * debug_uart = NULL); + + /** + * + * Activates a mode where the user can send text to and from the K64F + * debug Serial port directly to the WNC. The mode is entered via this + * call. The mode is exited when the user types CTRL-Q. While in this + * mode all text to and from the WNC is consumed by the debug Serial port. + * No other methods in the class will receive any of the WNC output. + * @ingroup API + * @param pUart - a pointer to a uart to use to collect the user input and put the output from the WNC. + * @param echoOn - set to true to echo what is input back to the output of pUart. + */ + bool enterWncTerminalMode(WncIO *pUart, bool echoOn); + +private: + + // Disallow copy + WncControllerK64F operator=(WncControllerK64F lhs); + + // Users must define these functionalities: + virtual int putc(char c); + virtual int puts(const char * s); + virtual char getc(void); + virtual int charReady(void); + virtual int dbgWriteChar(char b); + virtual int dbgWriteChars(const char *b); + virtual bool initWncModem(uint8_t powerUpTimeoutSecs); + virtual void waitMs(int t); + virtual void waitUs(int t); + + virtual int getLogTimerTicks(void); + virtual void startTimerA(void); + virtual void stopTimerA(void); + virtual int getTimerTicksA_mS(void); + virtual void startTimerB(void); + virtual void stopTimerB(void); + virtual int getTimerTicksB_mS(void); + + WNCDebug * m_pDbgUart; + WncIO * m_pWncUart; + WncGpioPinListK64F m_gpioPinList; + Timer m_logTimer; + Timer m_timerA; + Timer m_timerB; +}; + +}; // End namespace WncController_fk + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/ezconnect/wnc14a2a-driver/mbed_lib.json Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,13 @@ +{ + "name": "wnc14a2a-library", + "config": { + "wnc-debug": { + "help" : "enable or disable WNC debug messages.", + "value": "false" + }, + "wnc-debug_setting": { + "help" : "bit value 1 and/or 2 enable WncController debug output, bit value 4 enables mbed driver debug output.", + "value": "4" + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/mbedtls/awscerts.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,103 @@ + +/* + * AWS sample certificates + * + * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +const char aws_iot_certificate[] = \ + "-----BEGIN CERTIFICATE-----\n" + "MIIDWTCCAkGgAwIBAgIUHkXXxEbcTkd/uCFVlP1N6bIoBNMwDQYJKoZIhvcNAQEL\n" + "BQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g\n" + "SW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE4MDUxNjIxMzI1\n" + "OVoXDTQ5MTIzMTIzNTk1OVowHjEcMBoGA1UEAwwTQVdTIElvVCBDZXJ0aWZpY2F0\n" + "ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK9dcRj6Aap6+9Q1vWIr\n" + "09xkjlsY+wI9gaDat3e0o8zbnJZKb15yrxCfIltrM3argbRm6YCjgnhbiv+a6Km0\n" + "Lka+jI0++MA/NilQunqajRW65YxSq2g2vrqC8rn7XQorZWpw7gtVf3q+6KcfTA5R\n" + "0vMSyBIxOTXmqFXDB4qoVwWyBWTsSPQFBfOsDV8/ymVcSVS6XxROAMS5kYYnbRKg\n" + "dvdFZBK+bEH3162Dk419ZDbDjK7/kIsHVNzU/Pr8wKvvSQBxc72kGFcdgPy0r79Y\n" + "y0Qp1scMODQAO/qanLZJ52RSSjOn9Pkl+NCakh7r2RsHQ8SFkMNPDrRprwmM4JnD\n" + "BRkCAwEAAaNgMF4wHwYDVR0jBBgwFoAUn4hkgP+e66ZVT50BR4yohDMdTxMwHQYD\n" + "VR0OBBYEFHAJhc5z5amPzSUO4jzg30z8KRiYMAwGA1UdEwEB/wQCMAAwDgYDVR0P\n" + "AQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQCZ9WsL1+nv4maXuuq+IrhmF6lr\n" + "0Vza0s9a7/V5Z3Kr2Hvk0yuQWTLoEtc1PRDY/kIf2RYwOgkX+K41Z/BvC9IxRphh\n" + "hBIgjRzJRQe9eN/QKzDFO7D8wzxL8UfwFHgf+oTfsPf5T59C00A8WDii78XGDQfx\n" + "Bvcr6jX0GM1LZhdg0PkzQnpnl7N/zdwVTnOvOe3cYmrswjCdKHEe34HVnfeqr1v7\n" + "sTQF8h0AF2gube5szEL0Y6Y3Py9YoLxUHrizBRUXuvOUaS6i9OKFCCdJcSOK41As\n" + "Ql8zkG4wL3itYD6dC+p0bJGVYOwKArwNsR3G8hO+PhlCTsGf2pmiGpvXwTQc\n" + "-----END CERTIFICATE-----\n"; + +const char aws_iot_private_key[] = \ + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEowIBAAKCAQEAr11xGPoBqnr71DW9YivT3GSOWxj7Aj2BoNq3d7SjzNuclkpv\n" + "XnKvEJ8iW2szdquBtGbpgKOCeFuK/5roqbQuRr6MjT74wD82KVC6epqNFbrljFKr\n" + "aDa+uoLyuftdCitlanDuC1V/er7opx9MDlHS8xLIEjE5NeaoVcMHiqhXBbIFZOxI\n" + "9AUF86wNXz/KZVxJVLpfFE4AxLmRhidtEqB290VkEr5sQffXrYOTjX1kNsOMrv+Q\n" + "iwdU3NT8+vzAq+9JAHFzvaQYVx2A/LSvv1jLRCnWxww4NAA7+pqctknnZFJKM6f0\n" + "+SX40JqSHuvZGwdDxIWQw08OtGmvCYzgmcMFGQIDAQABAoIBACqr1xhXxU4BbQO/\n" + "K90Wgw/u3ADovDNYWhGVBu224+C2U4zygdT+nQCzaD3MgRJQO1h1bj4iC15UECeQ\n" + "PysEL/qA5Vx9RTQqbXhyL14j+06dUrvpZdIO+CwULxH4SzRtSNk0r4fMfx+UErKP\n" + "2Abn/Cevm0Vp+tzVyoJL2arwuXQl7LjJQxTmbjuRtnzEpir7pzJn7w8GOTHTr4bA\n" + "lT9+sfe9bX9agSKGTKKpI43KD3Tec+7CM6ZKcIZHRhHXO6ChUNlH7Kco9gHxbz95\n" + "fu9jeztTkmoEQ66WymTZC7ZG1jPqOBHIiyUmgGkP4qyEIiu3dimf5tXzaTcumFH7\n" + "knhvMIECgYEA6T/4I+tnQYUr9XADzlvA1OfFgQ2w8kT/zBv6n/JTUNa3aXzp3iYm\n" + "pa7zLfFjuj66zAmBTs8HPqFErBGZzgjGzMBJhDT/zkW4a+HrcKj9tE5rAj76oYpV\n" + "hINT1KiA+T6Lgo3kve/LFMpOjz9N1evifs9lg68znC0PKiuT7I+MvykCgYEAwHgk\n" + "Od46SJ0OMf9E7VrSuRWHYI4Ms/VNEcXOBfgfBU63mdYXVBeOXQR9xZOpl7/p6uUg\n" + "NfGFK68qeBh7TxsXo0xkZJuz/JSvynV1ZOT4NU20NQikzQ2jceGW8SktyOBGiAM/\n" + "FRrkCkEUMtV9o2GWr0SvB4KtcsarnJF/Mt82BHECgYEAsbqFiFEp95nSugzlSYtO\n" + "bqxAKSrWhnQe+VbkbJXH+FPzo5SD2zr0vdCGCZ7iX5rG2QvxFKzZl1ZorjgnGwBD\n" + "AucN3YhI1tL3SHT34ntR/d622RpF/Wsb6IHtEaBnzgNjaFJYVoXuF2Rj4fDZjURU\n" + "7E15y5JnX/xRjs9vwpRLKakCgYBF8iNwKFPUGceT4eohvDUCcFF/hIAXUllM0S77\n" + "Ol3DpwTQUl6t/9WL9lri17P1YxhWdihLkldMNd9gP71oVTc08Ek3yuIRM9Pi5ubf\n" + "4NpXPML11wtVPdSdSVd6kvWBoAfh3BQrzlb7ZwU5KB79pjSFw5c8SoBQSuUJBFNO\n" + "oJHX8QKBgCptXd++mR2Co37A7wAAB881AnZJbc+kGnEg1zszIG4zflINpQ+v0suK\n" + "+CceI00VPr1oH8VelVKxRHYOd7fIndT0lCEee6HgvEyQ7XzSBaNdHBLUHQub2eF2\n" + "k9eojnec1sXGr7wBcASvC4d9JtsO9Z9Co37ZqPCi263qlvWlhwfY\n" + "-----END RSA PRIVATE KEY-----\n"; + +const char aws_iot_rootCA[] = \ + "-----BEGIN CERTIFICATE-----\n" + "MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\n" + "yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\n" + "ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\n" + "U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\n" + "ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\n" + "aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL\n" + "MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW\n" + "ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln\n" + "biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp\n" + "U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y\n" + "aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1\n" + "nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex\n" + "t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz\n" + "SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG\n" + "BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+\n" + "rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/\n" + "NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E\n" + "BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH\n" + "BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\n" + "aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv\n" + "MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE\n" + "p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y\n" + "5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK\n" + "WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ\n" + "4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N\n" + "hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq\n" + "-----END CERTIFICATE-----\n"; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/mbedtls/be7945653d-certificate.pem.crt Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDWTCCAkGgAwIBAgIUHkXXxEbcTkd/uCFVlP1N6bIoBNMwDQYJKoZIhvcNAQEL +BQAwTTFLMEkGA1UECwxCQW1hem9uIFdlYiBTZXJ2aWNlcyBPPUFtYXpvbi5jb20g +SW5jLiBMPVNlYXR0bGUgU1Q9V2FzaGluZ3RvbiBDPVVTMB4XDTE4MDUxNjIxMzI1 +OVoXDTQ5MTIzMTIzNTk1OVowHjEcMBoGA1UEAwwTQVdTIElvVCBDZXJ0aWZpY2F0 +ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK9dcRj6Aap6+9Q1vWIr +09xkjlsY+wI9gaDat3e0o8zbnJZKb15yrxCfIltrM3argbRm6YCjgnhbiv+a6Km0 +Lka+jI0++MA/NilQunqajRW65YxSq2g2vrqC8rn7XQorZWpw7gtVf3q+6KcfTA5R +0vMSyBIxOTXmqFXDB4qoVwWyBWTsSPQFBfOsDV8/ymVcSVS6XxROAMS5kYYnbRKg +dvdFZBK+bEH3162Dk419ZDbDjK7/kIsHVNzU/Pr8wKvvSQBxc72kGFcdgPy0r79Y +y0Qp1scMODQAO/qanLZJ52RSSjOn9Pkl+NCakh7r2RsHQ8SFkMNPDrRprwmM4JnD +BRkCAwEAAaNgMF4wHwYDVR0jBBgwFoAUn4hkgP+e66ZVT50BR4yohDMdTxMwHQYD +VR0OBBYEFHAJhc5z5amPzSUO4jzg30z8KRiYMAwGA1UdEwEB/wQCMAAwDgYDVR0P +AQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4IBAQCZ9WsL1+nv4maXuuq+IrhmF6lr +0Vza0s9a7/V5Z3Kr2Hvk0yuQWTLoEtc1PRDY/kIf2RYwOgkX+K41Z/BvC9IxRphh +hBIgjRzJRQe9eN/QKzDFO7D8wzxL8UfwFHgf+oTfsPf5T59C00A8WDii78XGDQfx +Bvcr6jX0GM1LZhdg0PkzQnpnl7N/zdwVTnOvOe3cYmrswjCdKHEe34HVnfeqr1v7 +sTQF8h0AF2gube5szEL0Y6Y3Py9YoLxUHrizBRUXuvOUaS6i9OKFCCdJcSOK41As +Ql8zkG4wL3itYD6dC+p0bJGVYOwKArwNsR3G8hO+PhlCTsGf2pmiGpvXwTQc +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/mbedtls/be7945653d-private.pem.key Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAr11xGPoBqnr71DW9YivT3GSOWxj7Aj2BoNq3d7SjzNuclkpv +XnKvEJ8iW2szdquBtGbpgKOCeFuK/5roqbQuRr6MjT74wD82KVC6epqNFbrljFKr +aDa+uoLyuftdCitlanDuC1V/er7opx9MDlHS8xLIEjE5NeaoVcMHiqhXBbIFZOxI +9AUF86wNXz/KZVxJVLpfFE4AxLmRhidtEqB290VkEr5sQffXrYOTjX1kNsOMrv+Q +iwdU3NT8+vzAq+9JAHFzvaQYVx2A/LSvv1jLRCnWxww4NAA7+pqctknnZFJKM6f0 ++SX40JqSHuvZGwdDxIWQw08OtGmvCYzgmcMFGQIDAQABAoIBACqr1xhXxU4BbQO/ +K90Wgw/u3ADovDNYWhGVBu224+C2U4zygdT+nQCzaD3MgRJQO1h1bj4iC15UECeQ +PysEL/qA5Vx9RTQqbXhyL14j+06dUrvpZdIO+CwULxH4SzRtSNk0r4fMfx+UErKP +2Abn/Cevm0Vp+tzVyoJL2arwuXQl7LjJQxTmbjuRtnzEpir7pzJn7w8GOTHTr4bA +lT9+sfe9bX9agSKGTKKpI43KD3Tec+7CM6ZKcIZHRhHXO6ChUNlH7Kco9gHxbz95 +fu9jeztTkmoEQ66WymTZC7ZG1jPqOBHIiyUmgGkP4qyEIiu3dimf5tXzaTcumFH7 +knhvMIECgYEA6T/4I+tnQYUr9XADzlvA1OfFgQ2w8kT/zBv6n/JTUNa3aXzp3iYm +pa7zLfFjuj66zAmBTs8HPqFErBGZzgjGzMBJhDT/zkW4a+HrcKj9tE5rAj76oYpV +hINT1KiA+T6Lgo3kve/LFMpOjz9N1evifs9lg68znC0PKiuT7I+MvykCgYEAwHgk +Od46SJ0OMf9E7VrSuRWHYI4Ms/VNEcXOBfgfBU63mdYXVBeOXQR9xZOpl7/p6uUg +NfGFK68qeBh7TxsXo0xkZJuz/JSvynV1ZOT4NU20NQikzQ2jceGW8SktyOBGiAM/ +FRrkCkEUMtV9o2GWr0SvB4KtcsarnJF/Mt82BHECgYEAsbqFiFEp95nSugzlSYtO +bqxAKSrWhnQe+VbkbJXH+FPzo5SD2zr0vdCGCZ7iX5rG2QvxFKzZl1ZorjgnGwBD +AucN3YhI1tL3SHT34ntR/d622RpF/Wsb6IHtEaBnzgNjaFJYVoXuF2Rj4fDZjURU +7E15y5JnX/xRjs9vwpRLKakCgYBF8iNwKFPUGceT4eohvDUCcFF/hIAXUllM0S77 +Ol3DpwTQUl6t/9WL9lri17P1YxhWdihLkldMNd9gP71oVTc08Ek3yuIRM9Pi5ubf +4NpXPML11wtVPdSdSVd6kvWBoAfh3BQrzlb7ZwU5KB79pjSFw5c8SoBQSuUJBFNO +oJHX8QKBgCptXd++mR2Co37A7wAAB881AnZJbc+kGnEg1zszIG4zflINpQ+v0suK ++CceI00VPr1oH8VelVKxRHYOd7fIndT0lCEee6HgvEyQ7XzSBaNdHBLUHQub2eF2 +k9eojnec1sXGr7wBcASvC4d9JtsO9Z9Co37ZqPCi263qlvWlhwfY +-----END RSA PRIVATE KEY-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/mbedtls/be7945653d-public.pem.key Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr11xGPoBqnr71DW9YivT +3GSOWxj7Aj2BoNq3d7SjzNuclkpvXnKvEJ8iW2szdquBtGbpgKOCeFuK/5roqbQu +Rr6MjT74wD82KVC6epqNFbrljFKraDa+uoLyuftdCitlanDuC1V/er7opx9MDlHS +8xLIEjE5NeaoVcMHiqhXBbIFZOxI9AUF86wNXz/KZVxJVLpfFE4AxLmRhidtEqB2 +90VkEr5sQffXrYOTjX1kNsOMrv+QiwdU3NT8+vzAq+9JAHFzvaQYVx2A/LSvv1jL +RCnWxww4NAA7+pqctknnZFJKM6f0+SX40JqSHuvZGwdDxIWQw08OtGmvCYzgmcMF +GQIDAQAB +-----END PUBLIC KEY-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/mbedtls/network.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,223 @@ +/* + * TCP/IP or UDP/IP networking functions + * + * This version of net_sockets.c is setup to use ARM easy-connect for network connectivity + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include "mbed.h" + +#include "easy-connect.h" + +#define MBEDTLS_FS_IO 1 + +#include <stdbool.h> +#include <string.h> +#include <timer_platform.h> +#include <network_interface.h> + +#include "mbedtls/platform.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/pk.h" + +#if DEBUG_LEVEL > 0 +#include "mbedtls/debug.h" +#endif + +#include "aws_iot_error.h" +#include "aws_iot_log.h" +#include "network_interface.h" +#include "network_platform.h" + +#include "awscerts.h" + + +NetworkInterface *network = NULL; +TCPSocket mbedtls_socket; +bool network_connected = false; + +/* + * Initialize a context + */ +void mbedtls_aws_init( mbedtls_net_context *ctx ) +{ + FUNC_ENTRY; + + if( network != NULL ) + network->disconnect(); //disconnect from the current network + + network_connected = false; + network = easy_connect(true); + if (!network) { + IOT_DEBUG("Network Connection Failed!"); + return; + } + IOT_DEBUG("Modem SW Revision: %s", FIRMWARE_REV(network)); + network_connected = true; + ctx->fd = 1; +} + +/* + * Initiate a TCP connection with host:port and the given protocol + * return 0 if success, otherwise error is returned + */ +int mbedtls_aws_connect( mbedtls_net_context *ctx, const char *host, uint16_t port, int proto ) +{ + FUNC_ENTRY; + if( !network_connected ) { + IOT_DEBUG("No network connection"); + FUNC_EXIT_RC(NETWORK_ERR_NET_CONNECT_FAILED); + } + + int ret = mbedtls_socket.open(network) || mbedtls_socket.connect(host,port); + if( ret != 0 ){ + IOT_DEBUG("Socket Open Failed - %d",ret); + } + + FUNC_EXIT_RC(ret); +} + +/* + * Create a listening socket on bind_ip:port + */ +int mbedtls_aws_bind( mbedtls_net_context *ctx, const char *bind_ip, const char *port, int proto ) +{ + FUNC_EXIT_RC(MBEDTLS_ERR_NET_BIND_FAILED); +} + +/* + * Accept a connection from a remote client + */ +int mbedtls_aws_accept( mbedtls_net_context *bind_ctx, + mbedtls_net_context *client_ctx, + void *client_ip, size_t buf_size, size_t *ip_len ) +{ + FUNC_ENTRY; + FUNC_EXIT_RC(MBEDTLS_ERR_NET_ACCEPT_FAILED ); +} + +/* + * Set the socket blocking or non-blocking + */ +int mbedtls_aws_set_block( mbedtls_net_context *ctx ) +{ + mbedtls_socket.set_blocking(true); + return 0; +} + +int mbedtls_aws_set_nonblock( mbedtls_net_context *ctx ) +{ + mbedtls_socket.set_blocking(false); + return 0; +} + +/* + * Portable usleep helper + */ +void mbedtls_aws_usleep( unsigned long usec ) +{ + FUNC_ENTRY; + Timer t; + t.start(); + while( t.read_us() < (int)usec ) + /* wait here */ ; +} + +/* + * Read at most 'len' characters + */ +int mbedtls_aws_recv( void *ctx, unsigned char *buf, size_t len ) +{ + int ret; + int fd = ((mbedtls_net_context *) ctx)->fd; + + FUNC_ENTRY; + if( fd < 0 ) + FUNC_EXIT_RC(MBEDTLS_ERR_NET_INVALID_CONTEXT ); + + ret = (int) mbedtls_socket.recv( buf, len ); + + if( ret == NSAPI_ERROR_WOULD_BLOCK ) + ret = MBEDTLS_ERR_SSL_WANT_READ; + FUNC_EXIT_RC(ret ); +} + +/* + * Read at most 'len' characters, blocking for at most 'timeout' ms + */ +int mbedtls_aws_recv_timeout( void *ctx, unsigned char *buf, size_t len, uint32_t timeout ) +{ + int ret, ttime; + Timer t; + int fd = ((mbedtls_net_context *) ctx)->fd; + FUNC_ENTRY; + if( fd < 0 ) + FUNC_EXIT_RC(MBEDTLS_ERR_NET_INVALID_CONTEXT ); + + t.start(); + do { + ret = mbedtls_socket.recv( buf, len ); + ttime = t.read_ms(); + if( ret == 0 && ttime < (int)timeout ) + ret = mbedtls_socket.recv( buf, len ); + } + while( ttime < (int)timeout && ret == NSAPI_ERROR_WOULD_BLOCK ); + + if( ret < 0 && ttime >= (int)timeout ) + ret = MBEDTLS_ERR_SSL_TIMEOUT; + FUNC_EXIT_RC(ret); +} + +/* + * Write at most 'len' characters + */ +int mbedtls_aws_send( void *ctx, const unsigned char *buf, size_t len ) +{ + int ret; + int fd = ((mbedtls_net_context *) ctx)->fd; + + FUNC_ENTRY; + + if( fd < 0 ) + FUNC_EXIT_RC(MBEDTLS_ERR_NET_INVALID_CONTEXT ); + + while( (ret = mbedtls_socket.send(buf, len)) == NSAPI_ERROR_WOULD_BLOCK ) + /* keep trying */; + if( ret < 0 ) + ret = (ret == NSAPI_ERROR_WOULD_BLOCK )? MBEDTLS_ERR_SSL_WANT_WRITE : MBEDTLS_ERR_NET_SEND_FAILED; + FUNC_EXIT_RC( ret ); +} + +/* + * Gracefully close the connection + */ +void mbedtls_aws_free( mbedtls_net_context *ctx ) +{ + FUNC_ENTRY; + if( !network_connected || ctx->fd < 0 ) { + FUNC_EXIT; + } + + mbedtls_socket.close(); + network->disconnect(); //disconnect from the current network + ctx->fd = -1; + FUNC_EXIT; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/mbedtls/network_mbedtls_wrapper.cpp Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,434 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "mbed.h" + +#include "easy-connect.h" + +#define MBEDTLS_FS_IO 1 + +#include <stdbool.h> +#include <string.h> +#include <timer_platform.h> +#include <network_interface.h> + +#include "mbedtls/platform.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/x509_crt.h" +#include "mbedtls/pk.h" + +#if DEBUG_LEVEL > 0 +#include "mbedtls/debug.h" +#endif + +#include "aws_iot_error.h" +#include "aws_iot_log.h" +#include "network_interface.h" +#include "network_platform.h" + +#include "awscerts.h" + +/* This is the value used for ssl read timeout (in msec) */ +#define IOT_SSL_READ_TIMEOUT 10000 + +/* This defines the value of the debug buffer that gets allocated. + * The value can be altered based on memory constraints + */ +#ifdef ENABLE_IOT_DEBUG +#define MBEDTLS_DEBUG_BUFFER_SIZE 2048 +#endif + + +void mbedtls_aws_init( mbedtls_net_context * ); +int mbedtls_aws_connect( mbedtls_net_context *, const char *host, uint16_t port, int proto ); +int mbedtls_aws_bind( mbedtls_net_context *, const char *bind_ip, const char *port, int proto ); +int mbedtls_aws_accept( mbedtls_net_context *bind_ctx, mbedtls_net_context *client_ctx, void *client_ip, size_t buf_size, size_t *ip_len ) ; +void mbedtls_aws_usleep( unsigned long ); +int mbedtls_aws_recv( void *, unsigned char *, size_t ); +int mbedtls_aws_recv_timeout( void *, unsigned char *, size_t , uint32_t ); +int mbedtls_aws_send( void *, const unsigned char *, size_t ); +void mbedtls_aws_free( mbedtls_net_context * ); + +/* + * This is a function to do further verification if needed on the cert received + */ + +static int _iot_tls_verify_cert(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags) +{ + char buf[1024]; + ((void) data); + FUNC_ENTRY; + + IOT_DEBUG("Verify requested for (Depth %d):", depth); + mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt); + IOT_DEBUG("%s", buf); + + if((*flags) == 0) { + IOT_DEBUG("This certificate has no flags"); + } else { + IOT_DEBUG(buf, sizeof(buf), " ! ", *flags); + IOT_DEBUG("%s", buf); + } + + FUNC_EXIT_RC( 0); +} + +void _iot_tls_set_connect_params(Network *pNetwork, char *pRootCALocation, char *pDeviceCertLocation, + char *pDevicePrivateKeyLocation, char *pDestinationURL, + uint16_t destinationPort, uint32_t timeout_ms, bool ServerVerificationFlag) +{ + FUNC_ENTRY; + pNetwork->tlsConnectParams.DestinationPort = destinationPort; + pNetwork->tlsConnectParams.pDestinationURL = pDestinationURL; + pNetwork->tlsConnectParams.pDeviceCertLocation = pDeviceCertLocation; + pNetwork->tlsConnectParams.pDevicePrivateKeyLocation = pDevicePrivateKeyLocation; + pNetwork->tlsConnectParams.pRootCALocation = pRootCALocation; + pNetwork->tlsConnectParams.timeout_ms = timeout_ms; + pNetwork->tlsConnectParams.ServerVerificationFlag = ServerVerificationFlag; +} + +IoT_Error_t iot_tls_init(Network *pNetwork, char *pRootCALocation, char *pDeviceCertLocation, + char *pDevicePrivateKeyLocation, char *pDestinationURL, + uint16_t destinationPort, uint32_t timeout_ms, bool ServerVerificationFlag) +{ + FUNC_ENTRY; + _iot_tls_set_connect_params(pNetwork, pRootCALocation, pDeviceCertLocation, pDevicePrivateKeyLocation, + pDestinationURL, destinationPort, timeout_ms, ServerVerificationFlag); + + pNetwork->connect = iot_tls_connect; + pNetwork->read = iot_tls_read; + pNetwork->write = iot_tls_write; + pNetwork->disconnect = iot_tls_disconnect; + pNetwork->isConnected = iot_tls_is_connected; + pNetwork->destroy = iot_tls_destroy; + + pNetwork->tlsDataParams.flags = 0; + + FUNC_EXIT_RC( AWS_SUCCESS); +} + +IoT_Error_t iot_tls_is_connected(Network *pNetwork) +{ + FUNC_ENTRY; + /* Use this to add implementation which can check for physical layer disconnect */ + FUNC_EXIT_RC( NETWORK_PHYSICAL_LAYER_CONNECTED); +} + +IoT_Error_t iot_tls_connect(Network *pNetwork, TLSConnectParams *params) +{ + int ret = 0; + const char *pers = "aws_iot_tls_wrapper"; + TLSDataParams *tlsDataParams = NULL; + char vrfy_buf[512]; + const char *alpnProtocols[] = { "x-amzn-mqtt-ca", NULL }; + + FUNC_ENTRY; + + if(pNetwork == NULL) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if( params != NULL) { + _iot_tls_set_connect_params(pNetwork, params->pRootCALocation, params->pDeviceCertLocation, + params->pDevicePrivateKeyLocation, params->pDestinationURL, + params->DestinationPort, params->timeout_ms, params->ServerVerificationFlag); + } + + tlsDataParams = &(pNetwork->tlsDataParams); + + + mbedtls_entropy_init(&(tlsDataParams->entropy)); + mbedtls_ctr_drbg_init(&(tlsDataParams->ctr_drbg)); + mbedtls_x509_crt_init(&(tlsDataParams->clicert)); + mbedtls_x509_crt_init(&(tlsDataParams->cacert)); + mbedtls_ssl_init(&(tlsDataParams->ssl)); + mbedtls_ssl_config_init(&(tlsDataParams->conf)); + mbedtls_pk_init(&(tlsDataParams->pkey)); + + IOT_DEBUG("\nSeed the random number generator..."); + if((ret = mbedtls_ctr_drbg_seed(&(tlsDataParams->ctr_drbg), mbedtls_entropy_func, &(tlsDataParams->entropy), + (const unsigned char *) pers, + strlen(pers))) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ctr_drbg_seed returned -0x%x\n", -ret); + FUNC_EXIT_RC(NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED); + } + + IOT_DEBUG("Load the IoT certificate ..."); + #ifdef USING_SD_CARD + ret = mbedtls_x509_crt_parse_file(&(tlsDataParams->clicert), pNetwork->tlsConnectParams.pDeviceCertLocation); + #else + ret = mbedtls_x509_crt_parse(&(tlsDataParams->clicert), (const unsigned char*)aws_iot_certificate, + strlen(aws_iot_certificate)+1); + #endif + if(ret != 0) { + IOT_ERROR(" failed\n ! mbedtls_x509_crt_parse returned -0x%x while parsing device cert\n\n", -ret); + FUNC_EXIT_RC(NETWORK_X509_DEVICE_CRT_PARSE_ERROR); + } + + IOT_DEBUG("Load the private key ..."); + #ifdef USING_SD_CARD + ret = mbedtls_pk_parse_keyfile(&(tlsDataParams->pkey), pNetwork->tlsConnectParams.pDevicePrivateKeyLocation, ""); + #else + ret = mbedtls_pk_parse_key(&(tlsDataParams->pkey), (const unsigned char*)aws_iot_private_key, + strlen(aws_iot_private_key)+1, NULL, 0 ); + #endif + if(ret != 0) { + IOT_ERROR(" failed\n ! mbedtls_pk_parse_key returned -0x%x while parsing private key\n\n", -ret); + IOT_DEBUG(" path : %s ", pNetwork->tlsConnectParams.pDevicePrivateKeyLocation); + FUNC_EXIT_RC(NETWORK_PK_PRIVATE_KEY_PARSE_ERROR); + } + + IOT_DEBUG("Load the CA root certificate ..."); + #ifdef USING_SD_CARD + ret = mbedtls_x509_crt_parse_file(&(tlsDataParams->cacert), pNetwork->tlsConnectParams.pRootCALocation); + #else + ret = mbedtls_x509_crt_parse(&(tlsDataParams->cacert), (const unsigned char*)aws_iot_rootCA, + sizeof(aws_iot_rootCA)); + #endif + if(ret < 0) { + IOT_ERROR(" failed\n ! mbedtls_x509_crt_parse returned -0x%x while parsing root cert\n\n", -ret); + FUNC_EXIT_RC(NETWORK_X509_ROOT_CRT_PARSE_ERROR); + } + + IOT_DEBUG("done.\nSetting up the SSL/TLS structure..."); + if((ret = mbedtls_ssl_config_defaults(&(tlsDataParams->conf), + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ssl_config_defaults returned -0x%x\n\n", -ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + + mbedtls_ssl_conf_ca_chain(&(tlsDataParams->conf), &(tlsDataParams->cacert), NULL); + mbedtls_ssl_conf_rng(&(tlsDataParams->conf), mbedtls_ctr_drbg_random, &(tlsDataParams->ctr_drbg)); + + if((ret = mbedtls_ssl_setup(&(tlsDataParams->ssl), &(tlsDataParams->conf))) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ssl_setup returned -0x%x\n\n", -ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + + if((ret = mbedtls_ssl_set_hostname(&(tlsDataParams->ssl), pNetwork->tlsConnectParams.pDestinationURL)) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + IOT_DEBUG("Set the SSL BIO ..."); + mbedtls_ssl_set_bio(&(tlsDataParams->ssl), &(tlsDataParams->server_fd), mbedtls_aws_send, NULL, + mbedtls_aws_recv_timeout); + mbedtls_ssl_conf_verify(&(tlsDataParams->conf), _iot_tls_verify_cert, NULL); + if(pNetwork->tlsConnectParams.ServerVerificationFlag == true) + mbedtls_ssl_conf_authmode(&(tlsDataParams->conf), MBEDTLS_SSL_VERIFY_REQUIRED); + else + mbedtls_ssl_conf_authmode(&(tlsDataParams->conf), MBEDTLS_SSL_VERIFY_OPTIONAL); + + if((ret = mbedtls_ssl_conf_own_cert(&(tlsDataParams->conf), &(tlsDataParams->clicert), + &(tlsDataParams->pkey))) != 0) { + IOT_ERROR(" failed\n!!! mbedtls_ssl_conf_own_cert returned %d\n\n", ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + + mbedtls_ssl_conf_read_timeout(&(tlsDataParams->conf), pNetwork->tlsConnectParams.timeout_ms); + + /* Use the AWS IoT ALPN extension for MQTT if port 443 is requested. */ + if(443 == pNetwork->tlsConnectParams.DestinationPort) { + if((ret = mbedtls_ssl_conf_alpn_protocols(&(tlsDataParams->conf), alpnProtocols)) != 0) { + IOT_ERROR(" failed\n ! mbedtls_ssl_conf_alpn_protocols returned -0x%x\n\n", -ret); + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + } + + mbedtls_aws_init(&(tlsDataParams->server_fd)); + if( (tlsDataParams->server_fd).fd == -1 ) { + IOT_ERROR(" Network connected failed!\n"); + FUNC_EXIT_RC(NETWORK_ERR_NET_CONNECT_FAILED); + } + + IOT_DEBUG("Connecting to %s/%d...", pNetwork->tlsConnectParams.pDestinationURL, pNetwork->tlsConnectParams.DestinationPort); + if((ret = mbedtls_aws_connect(&(tlsDataParams->server_fd), pNetwork->tlsConnectParams.pDestinationURL, + pNetwork->tlsConnectParams.DestinationPort, + MBEDTLS_NET_PROTO_TCP)) != 0) { + IOT_ERROR(" failed\n ! mbedtls_aws_connect returned -0x%x\n\n", -ret); + switch(ret) { + case MBEDTLS_ERR_NET_SOCKET_FAILED: + FUNC_EXIT_RC(NETWORK_ERR_NET_SOCKET_FAILED); + case MBEDTLS_ERR_NET_UNKNOWN_HOST: + FUNC_EXIT_RC(NETWORK_ERR_NET_UNKNOWN_HOST); + case MBEDTLS_ERR_NET_CONNECT_FAILED: + default: + FUNC_EXIT_RC(NETWORK_ERR_NET_CONNECT_FAILED); + }; + } + + IOT_DEBUG("\n\nPerform the SSL/TLS handshake...\n\n\n\n"); + + while((ret = mbedtls_ssl_handshake(&(tlsDataParams->ssl))) != 0) { + if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + IOT_ERROR(" failed\n ! mbedtls_ssl_handshake returned -0x%x (%d)\n", -ret, ret); + if(ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) { + IOT_ERROR( "Unable to verify the server's certificate. Either it is invalid, or you\n" + "didn't set ca_file or ca_path to an appropriate value. Alternatively,\n" + "you may want to use auth_mode=optional for testing purposes.\n" ); + } + FUNC_EXIT_RC(SSL_CONNECTION_ERROR); + } + } + + IOT_DEBUG("[ Protocol is %s ]\n[ Ciphersuite is %s ]\n", mbedtls_ssl_get_version(&(tlsDataParams->ssl)), + mbedtls_ssl_get_ciphersuite(&(tlsDataParams->ssl))); + if((ret = mbedtls_ssl_get_record_expansion(&(tlsDataParams->ssl))) >= 0) { + IOT_DEBUG("[Record expansion is %d]", ret); + } + else{ + IOT_DEBUG("[Record expansion is unknown (compression)]"); + } + IOT_DEBUG("Verifying peer X.509 certificate..."); + + if(pNetwork->tlsConnectParams.ServerVerificationFlag == true) { + if((tlsDataParams->flags = mbedtls_ssl_get_verify_result(&(tlsDataParams->ssl))) != 0) { + IOT_ERROR(" FAIL\n"); + mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), " ! ", tlsDataParams->flags); + IOT_ERROR("%s\n", vrfy_buf); + ret = SSL_CONNECTION_ERROR; + } else { + IOT_DEBUG("Verified OK\n"); + ret = AWS_SUCCESS; + } + } else { + IOT_DEBUG("Server Verification skipped\n"); + ret = AWS_SUCCESS; + } + +#ifdef ENABLE_IOT_DEBUG + if (mbedtls_ssl_get_peer_cert(&(tlsDataParams->ssl)) != NULL) { + unsigned char buf[MBEDTLS_DEBUG_BUFFER_SIZE]; + IOT_DEBUG("Peer certificate information ...\n"); + mbedtls_x509_crt_info((char *) buf, sizeof(buf) - 1, " ", mbedtls_ssl_get_peer_cert(&(tlsDataParams->ssl))); + IOT_DEBUG("%s", buf); + } +#endif + + mbedtls_ssl_conf_read_timeout(&(tlsDataParams->conf), IOT_SSL_READ_TIMEOUT); + + FUNC_EXIT_RC((IoT_Error_t) ret); +} + +IoT_Error_t iot_tls_write(Network *pNetwork, unsigned char *pMsg, size_t len, awsTimer *timer, size_t *written_len) +{ + size_t written_so_far; + bool isErrorFlag = false; + int frags; + int ret = 0; + TLSDataParams *tlsDataParams = &(pNetwork->tlsDataParams); + + FUNC_ENTRY; + for(written_so_far = 0, frags = 0; + written_so_far < len && !has_timer_expired(timer); written_so_far += ret, frags++) { + while(!has_timer_expired(timer) && + (ret = mbedtls_ssl_write(&(tlsDataParams->ssl), pMsg + written_so_far, len - written_so_far)) <= 0) { + if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + IOT_ERROR("FAILED!\n ! mbedtls_ssl_write returned -0x%x\n\n", -ret); + /* All other negative return values indicate connection needs to be reset. + * Will be caught in ping request so ignored here */ + isErrorFlag = true; + break; + } + } + if(isErrorFlag) { + break; + } + } + + *written_len = written_so_far; + + if(isErrorFlag) { + FUNC_EXIT_RC(NETWORK_SSL_WRITE_ERROR); + } else if(has_timer_expired(timer) && written_so_far != len) { + FUNC_EXIT_RC(NETWORK_SSL_WRITE_TIMEOUT_ERROR); + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t iot_tls_read(Network *pNetwork, unsigned char *pMsg, size_t len, awsTimer *timer, size_t *read_len) +{ + mbedtls_ssl_context *ssl = &(pNetwork->tlsDataParams.ssl); + size_t rxLen = 0; + int ret=0; + FUNC_ENTRY; + + while (len > 0) { + // This read will timeout after IOT_SSL_READ_TIMEOUT if there's no data to be read + + while( ret == 0 && !has_timer_expired(timer) ) + ret = mbedtls_ssl_read(ssl, pMsg, len); + + if (ret > 0) { + rxLen += ret; + pMsg += ret; + len -= ret; + } + else if (ret == 0 || (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != MBEDTLS_ERR_SSL_TIMEOUT)) { + FUNC_EXIT_RC(NETWORK_SSL_READ_ERROR); + } + } + + if (len == 0) { + *read_len = rxLen; + FUNC_EXIT_RC(AWS_SUCCESS); + } + + if (rxLen == 0) { + FUNC_EXIT_RC(NETWORK_SSL_NOTHING_TO_READ); + } else { + FUNC_EXIT_RC(NETWORK_SSL_READ_TIMEOUT_ERROR); + } +} + +IoT_Error_t iot_tls_disconnect(Network *pNetwork) +{ + mbedtls_ssl_context *ssl = &(pNetwork->tlsDataParams.ssl); + int ret = 0; + FUNC_ENTRY; + do { + ret = mbedtls_ssl_close_notify(ssl); + } while(ret == MBEDTLS_ERR_SSL_WANT_WRITE); + + /* All other negative return values indicate connection needs to be reset. + * No further action required since this is disconnect call */ + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t iot_tls_destroy(Network *pNetwork) +{ + TLSDataParams *tlsDataParams = &(pNetwork->tlsDataParams); + FUNC_ENTRY; + + mbedtls_aws_free(&(tlsDataParams->server_fd)); + + mbedtls_x509_crt_free(&(tlsDataParams->clicert)); + mbedtls_x509_crt_free(&(tlsDataParams->cacert)); + mbedtls_pk_free(&(tlsDataParams->pkey)); + mbedtls_ssl_free(&(tlsDataParams->ssl)); + mbedtls_ssl_config_free(&(tlsDataParams->conf)); + mbedtls_ctr_drbg_free(&(tlsDataParams->ctr_drbg)); + mbedtls_entropy_free(&(tlsDataParams->entropy)); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/mbedtls/network_platform.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef IOTSDKC_NETWORK_MBEDTLS_PLATFORM_H_H + +#include "mbedtls/config.h" + +#include "mbedtls/platform.h" +#include "mbedtls/net.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/certs.h" +#include "mbedtls/x509.h" +#include "mbedtls/error.h" +#include "mbedtls/debug.h" +#include "mbedtls/timing.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLS Connection Parameters + * + * Defines a type containing TLS specific parameters to be passed down to the + * TLS networking layer to create a TLS secured socket. + */ +typedef struct _TLSDataParams { + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + uint32_t flags; + mbedtls_x509_crt cacert; + mbedtls_x509_crt clicert; + mbedtls_pk_context pkey; + mbedtls_net_context server_fd; +}TLSDataParams; + +#define IOTSDKC_NETWORK_MBEDTLS_PLATFORM_H_H + +#ifdef __cplusplus +} +#endif + +#endif //IOTSDKC_NETWORK_MBEDTLS_PLATFORM_H_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/mbedtls/rootCA.crt Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB +yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL +ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp +U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW +ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 +aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL +MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW +ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln +biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp +U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y +aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 +nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex +t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz +SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG +BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ +rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ +NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E +BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH +BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy +aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv +MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE +p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y +5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK +WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ +4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N +hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq +-----END CERTIFICATE-----
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/pthread/threads_platform.h Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "threads_interface.h" +#ifdef _ENABLE_THREAD_SUPPORT_ +#ifndef IOTSDKC_THREADS_PLATFORM_H_H +#define IOTSDKC_THREADS_PLATFORM_H_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <pthread.h> + +/** + * @brief Mutex Type + * + * definition of the Mutex struct. Platform specific + * + */ +struct _IoT_Mutex_t { + pthread_mutex_t lock; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* IOTSDKC_THREADS_PLATFORM_H_H */ +#endif /* _ENABLE_THREAD_SUPPORT_ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/platform/pthread/threads_pthread_wrapper.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,112 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "threads_platform.h" +#ifdef _ENABLE_THREAD_SUPPORT_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the provided mutex + * + * Call this function to initialize the mutex + * + * @param IoT_Mutex_t - pointer to the mutex to be initialized + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_init(IoT_Mutex_t *pMutex) { + if(0 != pthread_mutex_init(&(pMutex->lock), NULL)) { + return MUTEX_INIT_ERROR; + } + + return SUCCESS; +} + +/** + * @brief Lock the provided mutex + * + * Call this function to lock the mutex before performing a state change + * Blocking, thread will block until lock request fails + * + * @param IoT_Mutex_t - pointer to the mutex to be locked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_lock(IoT_Mutex_t *pMutex) { +int rc = pthread_mutex_lock(&(pMutex->lock)); + if(0 != rc) { + return MUTEX_LOCK_ERROR; + } + + return SUCCESS; +} + +/** + * @brief Try to lock the provided mutex + * + * Call this function to attempt to lock the mutex before performing a state change + * Non-Blocking, immediately returns with failure if lock attempt fails + * + * @param IoT_Mutex_t - pointer to the mutex to be locked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_trylock(IoT_Mutex_t *pMutex) { +int rc = pthread_mutex_trylock(&(pMutex->lock)); + if(0 != rc) { + return MUTEX_LOCK_ERROR; + } + + return SUCCESS; +} + +/** + * @brief Unlock the provided mutex + * + * Call this function to unlock the mutex before performing a state change + * + * @param IoT_Mutex_t - pointer to the mutex to be unlocked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_unlock(IoT_Mutex_t *pMutex) { + if(0 != pthread_mutex_unlock(&(pMutex->lock))) { + return MUTEX_UNLOCK_ERROR; + } + + return SUCCESS; +} + +/** + * @brief Destroy the provided mutex + * + * Call this function to destroy the mutex + * + * @param IoT_Mutex_t - pointer to the mutex to be destroyed + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_destroy(IoT_Mutex_t *pMutex) { + if(0 != pthread_mutex_destroy(&(pMutex->lock))) { + return MUTEX_DESTROY_ERROR; + } + + return SUCCESS; +} + +#ifdef __cplusplus +} +#endif + +#endif /* _ENABLE_THREAD_SUPPORT_ */ +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_jobs_interface.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,215 @@ +/* +* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "aws_iot_jobs_interface.h" +#include "aws_iot_log.h" +#include "aws_iot_jobs_json.h" +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK_GENERATE_STRING_RESULT(result, bufferSize) \ + if (result < 0) { \ + return FAILURE; \ + } else if ((unsigned) result >= bufferSize) { \ + return LIMIT_EXCEEDED_ERROR; \ + } + + +IoT_Error_t aws_iot_jobs_subscribe_to_job_messages( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const char *jobId, + AwsIotJobExecutionTopicType topicType, + AwsIotJobExecutionTopicReplyType replyType, + pApplicationHandler_t pApplicationHandler, + void *pApplicationHandlerData, + char *topicBuffer, + uint16_t topicBufferSize) +{ + int requiredSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, topicType, replyType, thingName, jobId); + CHECK_GENERATE_STRING_RESULT(requiredSize, topicBufferSize); + + return aws_iot_mqtt_subscribe(pClient, topicBuffer, (uint16_t)strlen(topicBuffer), qos, pApplicationHandler, pApplicationHandlerData); +} + +IoT_Error_t aws_iot_jobs_subscribe_to_all_job_messages( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + pApplicationHandler_t pApplicationHandler, + void *pApplicationHandlerData, + char *topicBuffer, + uint16_t topicBufferSize) +{ + return aws_iot_jobs_subscribe_to_job_messages(pClient, qos, thingName, NULL, JOB_WILDCARD_TOPIC, JOB_WILDCARD_REPLY_TYPE, + pApplicationHandler, pApplicationHandlerData, topicBuffer, topicBufferSize); +} + +IoT_Error_t aws_iot_jobs_unsubscribe_from_job_messages( + AWS_IoT_Client *pClient, + char *topicBuffer) +{ + return aws_iot_mqtt_unsubscribe(pClient, topicBuffer, (uint16_t)strlen(topicBuffer)); +} + +IoT_Error_t aws_iot_jobs_send_query( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const char *jobId, + const char *clientToken, + char *topicBuffer, + uint16_t topicBufferSize, + char *messageBuffer, + size_t messageBufferSize, + AwsIotJobExecutionTopicType topicType) +{ + if (thingName == NULL || topicBuffer == NULL || (clientToken != NULL && messageBuffer == NULL)) { + return NULL_VALUE_ERROR; + } + + int neededSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, topicType, JOB_REQUEST_TYPE, thingName, jobId); + CHECK_GENERATE_STRING_RESULT(neededSize, topicBufferSize); + uint16_t topicSize = (uint16_t) neededSize; + + char emptyBuffer[1]; + size_t messageLength; + if (clientToken == NULL) { + messageLength = 0; + messageBuffer = emptyBuffer; + } else { + int serializeResult = aws_iot_jobs_json_serialize_client_token_only_request(messageBuffer, messageBufferSize, clientToken); + CHECK_GENERATE_STRING_RESULT(serializeResult, messageBufferSize); + messageLength = (size_t)serializeResult; + } + + IoT_Publish_Message_Params publishParams; + publishParams.qos = qos; + publishParams.isRetained = 0; + publishParams.isDup = 0; + publishParams.id = 0; + publishParams.payload = messageBuffer; + publishParams.payloadLen = messageLength; + + return aws_iot_mqtt_publish(pClient, topicBuffer, topicSize, &publishParams); +} + +IoT_Error_t aws_iot_jobs_start_next( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const AwsIotStartNextPendingJobExecutionRequest *startNextRequest, + char *topicBuffer, + uint16_t topicBufferSize, + char *messageBuffer, + size_t messageBufferSize) +{ + if (thingName == NULL || topicBuffer == NULL || startNextRequest == NULL) { + return NULL_VALUE_ERROR; + } + + int neededSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, JOB_START_NEXT_TOPIC, JOB_REQUEST_TYPE, thingName, NULL); + CHECK_GENERATE_STRING_RESULT(neededSize, topicBufferSize); + uint16_t topicSize = (uint16_t) neededSize; + + int serializeResult = aws_iot_jobs_json_serialize_start_next_job_execution_request(messageBuffer, messageBufferSize, startNextRequest); + CHECK_GENERATE_STRING_RESULT(serializeResult, messageBufferSize); + + IoT_Publish_Message_Params publishParams; + publishParams.qos = qos; + publishParams.isRetained = 0; + publishParams.isDup = 0; + publishParams.id = 0; + publishParams.payload = messageBuffer; + publishParams.payloadLen = (size_t) serializeResult; + + return aws_iot_mqtt_publish(pClient, topicBuffer, topicSize, &publishParams); +} + +IoT_Error_t aws_iot_jobs_describe( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const char *jobId, + const AwsIotDescribeJobExecutionRequest *describeRequest, + char *topicBuffer, + uint16_t topicBufferSize, + char *messageBuffer, + size_t messageBufferSize) +{ + if (thingName == NULL || topicBuffer == NULL || describeRequest == NULL) { + return NULL_VALUE_ERROR; + } + + int neededSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, JOB_DESCRIBE_TOPIC, JOB_REQUEST_TYPE, thingName, jobId); + CHECK_GENERATE_STRING_RESULT(neededSize, topicBufferSize); + uint16_t topicSize = (uint16_t) neededSize; + + char emptyBuffer[1]; + size_t messageLength; + if (messageBuffer == NULL) { + messageLength = 0; + messageBuffer = emptyBuffer; + } else { + int serializeResult = aws_iot_jobs_json_serialize_describe_job_execution_request(messageBuffer, messageBufferSize, describeRequest); + CHECK_GENERATE_STRING_RESULT(serializeResult, messageBufferSize); + messageLength = (size_t) serializeResult; + } + + IoT_Publish_Message_Params publishParams; + publishParams.qos = qos; + publishParams.isRetained = 0; + publishParams.isDup = 0; + publishParams.id = 0; + publishParams.payload = messageBuffer; + publishParams.payloadLen = messageLength; + + return aws_iot_mqtt_publish(pClient, topicBuffer, topicSize, &publishParams); +} + +IoT_Error_t aws_iot_jobs_send_update( + AWS_IoT_Client *pClient, QoS qos, + const char *thingName, + const char *jobId, + const AwsIotJobExecutionUpdateRequest *updateRequest, + char *topicBuffer, + uint16_t topicBufferSize, + char *messageBuffer, + size_t messageBufferSize) +{ + if (thingName == NULL || topicBuffer == NULL || jobId == NULL || updateRequest == NULL) { + return NULL_VALUE_ERROR; + } + + int neededSize = aws_iot_jobs_get_api_topic(topicBuffer, topicBufferSize, JOB_UPDATE_TOPIC, JOB_REQUEST_TYPE, thingName, jobId); + CHECK_GENERATE_STRING_RESULT(neededSize, topicBufferSize); + uint16_t topicSize = (uint16_t) neededSize; + + int serializeResult = aws_iot_jobs_json_serialize_update_job_execution_request(messageBuffer, messageBufferSize, updateRequest); + CHECK_GENERATE_STRING_RESULT(serializeResult, messageBufferSize); + + IoT_Publish_Message_Params publishParams; + publishParams.qos = qos; + publishParams.isRetained = 0; + publishParams.isDup = 0; + publishParams.id = 0; + publishParams.payload = messageBuffer; + publishParams.payloadLen = (size_t) serializeResult; + + return aws_iot_mqtt_publish(pClient, topicBuffer, topicSize, &publishParams); +} + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_jobs_json.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,197 @@ +/* +* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#define __STDC_FORMAT_MACROS +#include <string.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdio.h> +#include <stdarg.h> + +#include "jsmn.h" +#include "aws_iot_jobs_json.h" + +struct _SerializeState { + int totalSize; + char *nextPtr; + size_t remaingSize; +}; + +static void _printToBuffer(struct _SerializeState *state, const char *fmt, ...) { + if (state->totalSize == -1) return; + + va_list vl; + va_start(vl, fmt); + int len = vsnprintf(state->nextPtr, state->remaingSize, fmt, vl); + if (len < 0) { + state->totalSize = -1; + } else { + state->totalSize += len; + if (state->nextPtr != NULL) { + if (state->remaingSize > (size_t) len) { + state->remaingSize -= (size_t) len; + state->nextPtr += len; + } else { + state->remaingSize = 0; + state->nextPtr = NULL; + } + } + } + va_end(vl); +} + +static void _printKey(struct _SerializeState *state, bool first, const char *key) { + if (first) { + _printToBuffer(state, "{\"%s\":", key); + } else { + _printToBuffer(state, ",\"%s\":", key); + } +} + +static void _printStringValue(struct _SerializeState *state, const char *value) { + if (value == NULL) { + _printToBuffer(state, "null"); + } else { + _printToBuffer(state, "\"%s\"", value); + } +} + +static void _printLongValue(struct _SerializeState *state, int64_t value) { + _printToBuffer(state, "%lld", value); +} + +static void _printBooleanValue(struct _SerializeState *state, bool value) { + if(value) { + _printToBuffer(state, "true"); + } else { + _printToBuffer(state, "false"); + } +} + +int aws_iot_jobs_json_serialize_update_job_execution_request( + char *requestBuffer, size_t bufferSize, + const AwsIotJobExecutionUpdateRequest *request) +{ + const char *statusStr = aws_iot_jobs_map_status_to_string(request->status); + if (statusStr == NULL) return -1; + if (requestBuffer == NULL) bufferSize = 0; + + struct _SerializeState state = { 0, requestBuffer, bufferSize }; + _printKey(&state, true, "status"); + _printStringValue(&state, statusStr); + if (request->statusDetails != NULL) { + _printKey(&state, false, "statusDetails"); + _printToBuffer(&state, "%s", request->statusDetails); + } + if (request->executionNumber != 0) { + _printKey(&state, false, "executionNumber"); + _printLongValue(&state, request->executionNumber); + } + if (request->expectedVersion != 0) { + _printKey(&state, false, "expectedVersion"); + _printLongValue(&state, request->expectedVersion); + } + if (request->includeJobExecutionState) { + _printKey(&state, false, "includeJobExecutionState"); + _printBooleanValue(&state, request->includeJobExecutionState); + } + if (request->includeJobDocument) { + _printKey(&state, false, "includeJobDocument"); + _printBooleanValue(&state, request->includeJobDocument); + } + if (request->clientToken != NULL) { + _printKey(&state, false, "clientToken"); + _printStringValue(&state, request->clientToken); + } + + _printToBuffer(&state, "}"); + + return state.totalSize; +} + +int aws_iot_jobs_json_serialize_client_token_only_request( + char *requestBuffer, size_t bufferSize, + const char *clientToken) +{ + struct _SerializeState state = { 0, requestBuffer, bufferSize }; + _printKey(&state, true, "clientToken"); + _printStringValue(&state, clientToken); + _printToBuffer(&state, "}"); + + return state.totalSize; +} + +int aws_iot_jobs_json_serialize_describe_job_execution_request( + char *requestBuffer, size_t bufferSize, + const AwsIotDescribeJobExecutionRequest *request) +{ + bool first = true; + + if (requestBuffer == NULL) return 0; + + struct _SerializeState state = { 0, requestBuffer, bufferSize }; + if (request->clientToken != NULL) { + _printKey(&state, first, "clientToken"); + _printStringValue(&state, request->clientToken); + first = false; + } + if (request->executionNumber != 0) { + _printKey(&state, first, "executionNumber"); + _printLongValue(&state, request->executionNumber); + first = false; + } + if (request->includeJobDocument) { + _printKey(&state, first, "includeJobDocument"); + _printBooleanValue(&state, request->includeJobDocument); + } + + _printToBuffer(&state, "}"); + + return state.totalSize; +} + +int aws_iot_jobs_json_serialize_start_next_job_execution_request( + char *requestBuffer, size_t bufferSize, + const AwsIotStartNextPendingJobExecutionRequest *request) +{ + if (requestBuffer == NULL) bufferSize = 0; + struct _SerializeState state = { 0, requestBuffer, bufferSize }; + if (request->statusDetails != NULL) { + _printKey(&state, true, "statusDetails"); + _printToBuffer(&state, "%s", request->statusDetails); + } + if (request->clientToken != NULL) { + if(request->statusDetails != NULL) { + _printKey(&state, false, "clientToken"); + } else { + _printKey(&state, true, "clientToken"); + } + _printStringValue(&state, request->clientToken); + } + if (request->clientToken == NULL && request->statusDetails == NULL) { + _printToBuffer(&state, "{"); + } + _printToBuffer(&state, "}"); + return state.totalSize; +} + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_jobs_topics.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,129 @@ +/* +* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_jobs_topics.h" +#include <string.h> +#include <stdio.h> +#include <stdbool.h> + +#define BASE_THINGS_TOPIC "$aws/things/" + +#define NOTIFY_OPERATION "notify" +#define NOTIFY_NEXT_OPERATION "notify-next" +#define GET_OPERATION "get" +#define START_NEXT_OPERATION "start-next" +#define WILDCARD_OPERATION "+" +#define UPDATE_OPERATION "update" +#define ACCEPTED_REPLY "accepted" +#define REJECTED_REPLY "rejected" +#define WILDCARD_REPLY "+" + +static const char *_get_operation_for_base_topic(AwsIotJobExecutionTopicType topicType) { + switch (topicType) { + case JOB_UPDATE_TOPIC: + return UPDATE_OPERATION; + case JOB_NOTIFY_TOPIC: + return NOTIFY_OPERATION; + case JOB_NOTIFY_NEXT_TOPIC: + return NOTIFY_NEXT_OPERATION; + case JOB_GET_PENDING_TOPIC: + case JOB_DESCRIBE_TOPIC: + return GET_OPERATION; + case JOB_START_NEXT_TOPIC: + return START_NEXT_OPERATION; + case JOB_WILDCARD_TOPIC: + return WILDCARD_OPERATION; + case JOB_UNRECOGNIZED_TOPIC: + default: + return NULL; + } +} + +static bool _base_topic_requires_job_id(AwsIotJobExecutionTopicType topicType) { + switch (topicType) { + case JOB_UPDATE_TOPIC: + case JOB_DESCRIBE_TOPIC: + return true; + case JOB_NOTIFY_TOPIC: + case JOB_NOTIFY_NEXT_TOPIC: + case JOB_START_NEXT_TOPIC: + case JOB_GET_PENDING_TOPIC: + case JOB_WILDCARD_TOPIC: + case JOB_UNRECOGNIZED_TOPIC: + default: + return false; + } +} + +static const char *_get_suffix_for_topic_type(AwsIotJobExecutionTopicReplyType replyType) { + switch (replyType) { + case JOB_REQUEST_TYPE: + return ""; +// break; + case JOB_ACCEPTED_REPLY_TYPE: + return "/" ACCEPTED_REPLY; +// break; + case JOB_REJECTED_REPLY_TYPE: + return "/" REJECTED_REPLY; +// break; + case JOB_WILDCARD_REPLY_TYPE: + return "/" WILDCARD_REPLY; +// break; + case JOB_UNRECOGNIZED_TOPIC_TYPE: + default: + return NULL; + } +} + +int aws_iot_jobs_get_api_topic(char *buffer, size_t bufferSize, + AwsIotJobExecutionTopicType topicType, AwsIotJobExecutionTopicReplyType replyType, + const char* thingName, const char* jobId) +{ + if (thingName == NULL) { + return -1; + } + + if ((topicType == JOB_NOTIFY_TOPIC || topicType == JOB_NOTIFY_NEXT_TOPIC) && replyType != JOB_REQUEST_TYPE) { + return -1; + } + + bool requireJobId = _base_topic_requires_job_id(topicType); + if (jobId == NULL && requireJobId) { + return -1; + } + + const char *operation = _get_operation_for_base_topic(topicType); + if (operation == NULL) { + return -1; + } + + const char *suffix = _get_suffix_for_topic_type(replyType); + + if (requireJobId || (topicType == JOB_WILDCARD_TOPIC && jobId != NULL)) { + return snprintf(buffer, bufferSize, BASE_THINGS_TOPIC "%s/jobs/%s/%s%s", thingName, jobId, operation, suffix); + } else if (topicType == JOB_WILDCARD_TOPIC) { + return snprintf(buffer, bufferSize, BASE_THINGS_TOPIC "%s/jobs/#", thingName); + } else { + return snprintf(buffer, bufferSize, BASE_THINGS_TOPIC "%s/jobs/%s%s", thingName, operation, suffix); + } +} + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_jobs_types.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,73 @@ +/* +* Copyright 2015-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <string.h> +#include "aws_iot_jobs_types.h" + +const char *JOB_EXECUTION_QUEUED_STR = "QUEUED"; +const char *JOB_EXECUTION_IN_PROGRESS_STR = "IN_PROGRESS"; +const char *JOB_EXECUTION_FAILED_STR = "FAILED"; +const char *JOB_EXECUTION_SUCCEEDED_STR = "SUCCEEDED"; +const char *JOB_EXECUTION_CANCELED_STR = "CANCELED"; +const char *JOB_EXECUTION_REJECTED_STR = "REJECTED"; + +JobExecutionStatus aws_iot_jobs_map_string_to_job_status(const char *str) { + if (str == NULL || str[0] == '\0') { + return JOB_EXECUTION_STATUS_NOT_SET; + } else if (strcmp(str, JOB_EXECUTION_QUEUED_STR) == 0) { + return JOB_EXECUTION_QUEUED; + } else if(strcmp(str, JOB_EXECUTION_IN_PROGRESS_STR) == 0) { + return JOB_EXECUTION_IN_PROGRESS; + } else if(strcmp(str, JOB_EXECUTION_FAILED_STR) == 0) { + return JOB_EXECUTION_FAILED; + } else if(strcmp(str, JOB_EXECUTION_SUCCEEDED_STR) == 0) { + return JOB_EXECUTION_SUCCEEDED; + } else if(strcmp(str, JOB_EXECUTION_CANCELED_STR) == 0) { + return JOB_EXECUTION_CANCELED; + } else if(strcmp(str, JOB_EXECUTION_REJECTED_STR) == 0) { + return JOB_EXECUTION_REJECTED; + } else { + return JOB_EXECUTION_UNKNOWN_STATUS; + } +} + +const char *aws_iot_jobs_map_status_to_string(JobExecutionStatus status) { + switch(status) { + case JOB_EXECUTION_QUEUED: + return JOB_EXECUTION_QUEUED_STR; + case JOB_EXECUTION_IN_PROGRESS: + return JOB_EXECUTION_IN_PROGRESS_STR; + case JOB_EXECUTION_FAILED: + return JOB_EXECUTION_FAILED_STR; + case JOB_EXECUTION_SUCCEEDED: + return JOB_EXECUTION_SUCCEEDED_STR; + case JOB_EXECUTION_CANCELED: + return JOB_EXECUTION_CANCELED_STR; + case JOB_EXECUTION_REJECTED: + return JOB_EXECUTION_REJECTED_STR; + case JOB_EXECUTION_STATUS_NOT_SET: + case JOB_EXECUTION_UNKNOWN_STATUS: + default: + return NULL; + } +} + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_json_utils.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,227 @@ +/* + * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_json_utils.c + * @brief Utilities for manipulating JSON + * + * json_utils provides JSON parsing utilities for use with the IoT SDK. + * Underlying JSON parsing relies on the Jasmine JSON parser. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_json_utils.h" + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include "aws_iot_log.h" + +int8_t jsoneq(const char *json, jsmntok_t *tok, const char *s) { + if(tok->type == JSMN_STRING) { + if((int) strlen(s) == tok->end - tok->start) { + if(strncmp(json + tok->start, s, (size_t) (tok->end - tok->start)) == 0) { + return 0; + } + } + } + return -1; +} + +IoT_Error_t parseUnsignedInteger32Value(uint32_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + if(('-' == (char) (jsonString[token->start])) || (1 != sscanf(jsonString + token->start, "%lu", i))) { + IOT_WARN("Token was not an unsigned integer."); + return JSON_PARSE_ERROR; + } + + return AWS_SUCCESS; +} + +IoT_Error_t parseUnsignedInteger16Value(uint16_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + if(('-' == (char) (jsonString[token->start])) || (1 != sscanf(jsonString + token->start, "%hu", i))) { + IOT_WARN("Token was not an unsigned integer."); + return JSON_PARSE_ERROR; + } + + return AWS_SUCCESS; +} + +IoT_Error_t parseUnsignedInteger8Value(uint8_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + if(('-' == (char) (jsonString[token->start])) || (1 != sscanf(jsonString + token->start, "%hhu", i))) { + IOT_WARN("Token was not an unsigned integer."); + return JSON_PARSE_ERROR; + } + + return AWS_SUCCESS; +} + +IoT_Error_t parseInteger32Value(int32_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + if(1 != sscanf(jsonString + token->start, "%li", i)) { + IOT_WARN("Token was not an integer."); + return JSON_PARSE_ERROR; + } + + return AWS_SUCCESS; +} + +IoT_Error_t parseInteger16Value(int16_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + if(1 != sscanf(jsonString + token->start, "%hi", i)) { + IOT_WARN("Token was not an integer."); + return JSON_PARSE_ERROR; + } + + return AWS_SUCCESS; +} + +IoT_Error_t parseInteger8Value(int8_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + if(1 != sscanf(jsonString + token->start, "%hhi", i)) { + IOT_WARN("Token was not an integer."); + return JSON_PARSE_ERROR; + } + + return AWS_SUCCESS; +} + +IoT_Error_t parseFloatValue(float *f, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not a float."); + return JSON_PARSE_ERROR; + } + + if(1 != sscanf(jsonString + token->start, "%f", f)) { + IOT_WARN("Token was not a float."); + return JSON_PARSE_ERROR; + } + + return AWS_SUCCESS; +} + +IoT_Error_t parseDoubleValue(double *d, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not a double."); + return JSON_PARSE_ERROR; + } + + if(1 != sscanf(jsonString + token->start, "%lf", d)) { + IOT_WARN("Token was not a double."); + return JSON_PARSE_ERROR; + } + + return AWS_SUCCESS; +} + +IoT_Error_t parseBooleanValue(bool *b, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not a primitive."); + return JSON_PARSE_ERROR; + } + if(strncmp(jsonString + token->start, "true", 4) == 0) { + *b = true; + } else if(strncmp(jsonString + token->start, "false", 5) == 0) { + *b = false; + } else { + IOT_WARN("Token was not a bool."); + return JSON_PARSE_ERROR; + } + return AWS_SUCCESS; +} + +IoT_Error_t parseStringValue(char *buf, size_t bufLen, const char *jsonString, jsmntok_t *token) { + /* This length does not include a null-terminator. */ + size_t stringLength = (size_t)(token->end - token->start); + + if(token->type != JSMN_STRING) { + IOT_WARN("Token was not a string."); + return JSON_PARSE_ERROR; + } + + if (stringLength+1 > bufLen) { + IOT_WARN("Buffer too small to hold string value."); + return SHADOW_JSON_ERROR; + } + + strncpy(buf, jsonString + token->start, stringLength); + buf[stringLength] = '\0'; + + return AWS_SUCCESS; +} + +jsmntok_t *findToken(const char *key, const char *jsonString, jsmntok_t *token) { + jsmntok_t *result = token; + int i; + + if(token->type != JSMN_OBJECT) { + IOT_WARN("Token was not an object."); + return NULL; + } + + if(token->size == 0) { + return NULL; + } + + result = token + 1; + + for (i = 0; i < token->size; i++) { + if (0 == jsoneq(jsonString, result, key)) { + return result + 1; + } + + int propertyEnd = (result + 1)->end; + result += 2; + while (result->start < propertyEnd) + result++; + } + + return NULL; +} + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_mqtt_client.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,368 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client.c + * @brief MQTT client API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <string.h> + +#include "aws_iot_log.h" +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_version.h" + +#if !DISABLE_METRICS +#define SDK_METRICS_LEN 25 +#define SDK_METRICS_TEMPLATE "?SDK=C&Version=%d.%d.%d" +static char pUsernameTemp[SDK_METRICS_LEN] = {0}; +#endif + +#ifdef _ENABLE_THREAD_SUPPORT_ +#include "threads_interface.h" +#endif + +const IoT_Client_Init_Params iotClientInitParamsDefault = IoT_Client_Init_Params_initializer; +const IoT_MQTT_Will_Options iotMqttWillOptionsDefault = IoT_MQTT_Will_Options_Initializer; +const IoT_Client_Connect_Params iotClientConnectParamsDefault = IoT_Client_Connect_Params_initializer; + +ClientState aws_iot_mqtt_get_client_state(AWS_IoT_Client *pClient) { + FUNC_ENTRY; + if(NULL == pClient) { + return CLIENT_STATE_INVALID; + } + + FUNC_EXIT_RC(pClient->clientStatus.clientState); +} + +#ifdef _ENABLE_THREAD_SUPPORT_ +IoT_Error_t aws_iot_mqtt_client_lock_mutex(AWS_IoT_Client *pClient, IoT_Mutex_t *pMutex) { + FUNC_ENTRY; + IoT_Error_t threadRc = FAILURE; + + if(NULL == pClient || NULL == pMutex){ + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(false == pClient->clientData.isBlockOnThreadLockEnabled) { + threadRc = aws_iot_thread_mutex_trylock(pMutex); + } else { + threadRc = aws_iot_thread_mutex_lock(pMutex); + /* Should never return Error because the above request blocks until lock is obtained */ + } + + if(AWS_SUCCESS != threadRc) { + FUNC_EXIT_RC(threadRc); + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_client_unlock_mutex(AWS_IoT_Client *pClient, IoT_Mutex_t *pMutex) { + if(NULL == pClient || NULL == pMutex) { + return NULL_VALUE_ERROR; + } + IOT_UNUSED(pClient); + return aws_iot_thread_mutex_unlock(pMutex); +} +#endif + +IoT_Error_t aws_iot_mqtt_set_client_state(AWS_IoT_Client *pClient, ClientState expectedCurrentState, + ClientState newState) { + IoT_Error_t rc; +#ifdef _ENABLE_THREAD_SUPPORT_ + IoT_Error_t threadRc = FAILURE; +#endif + + FUNC_ENTRY; + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + rc = aws_iot_mqtt_client_lock_mutex(pClient, &(pClient->clientData.state_change_mutex)); + if(AWS_SUCCESS != rc) { + return rc; + } +#endif + if(expectedCurrentState == aws_iot_mqtt_get_client_state(pClient)) { + pClient->clientStatus.clientState = newState; + rc = AWS_SUCCESS; + } else { + rc = MQTT_UNEXPECTED_CLIENT_STATE_ERROR; + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + threadRc = aws_iot_mqtt_client_unlock_mutex(pClient, &(pClient->clientData.state_change_mutex)); + if(AWS_SUCCESS == rc && AWS_SUCCESS != threadRc) { + rc = threadRc; + } +#endif + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_mqtt_set_connect_params(AWS_IoT_Client *pClient, IoT_Client_Connect_Params *pNewConnectParams) { + FUNC_ENTRY; + if(NULL == pClient || NULL == pNewConnectParams) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + pClient->clientData.options.isWillMsgPresent = pNewConnectParams->isWillMsgPresent; + pClient->clientData.options.MQTTVersion = pNewConnectParams->MQTTVersion; + pClient->clientData.options.pClientID = pNewConnectParams->pClientID; + pClient->clientData.options.clientIDLen = pNewConnectParams->clientIDLen; +#if !DISABLE_METRICS + if (0 == strlen(pUsernameTemp)) { + snprintf(pUsernameTemp, SDK_METRICS_LEN, SDK_METRICS_TEMPLATE, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); + } + pClient->clientData.options.pUsername = (char*)&pUsernameTemp[0]; + pClient->clientData.options.usernameLen = strlen(pUsernameTemp); +#else + pClient->clientData.options.pUsername = pNewConnectParams->pUsername; + pClient->clientData.options.usernameLen = pNewConnectParams->usernameLen; +#endif + pClient->clientData.options.pPassword = pNewConnectParams->pPassword; + pClient->clientData.options.passwordLen = pNewConnectParams->passwordLen; + pClient->clientData.options.will.pTopicName = pNewConnectParams->will.pTopicName; + pClient->clientData.options.will.topicNameLen = pNewConnectParams->will.topicNameLen; + pClient->clientData.options.will.pMessage = pNewConnectParams->will.pMessage; + pClient->clientData.options.will.msgLen = pNewConnectParams->will.msgLen; + pClient->clientData.options.will.qos = pNewConnectParams->will.qos; + pClient->clientData.options.will.isRetained = pNewConnectParams->will.isRetained; + pClient->clientData.options.keepAliveIntervalInSec = pNewConnectParams->keepAliveIntervalInSec; + pClient->clientData.options.isCleanSession = pNewConnectParams->isCleanSession; + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_free(AWS_IoT_Client *pClient) +{ + IoT_Error_t rc = AWS_SUCCESS; + + if (NULL == pClient) { + rc = NULL_VALUE_ERROR; + }else + { + #ifdef _ENABLE_THREAD_SUPPORT_ + if (rc == AWS_SUCCESS) + { + rc = aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex)); + } + + if (rc == AWS_SUCCESS) + { + rc = aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_read_mutex)); + }else{ + (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_read_mutex)); + } + + if (rc == AWS_SUCCESS) + { + rc = aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_write_mutex)); + }else{ + (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_write_mutex)); + } + #endif + } + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_mqtt_init(AWS_IoT_Client *pClient, IoT_Client_Init_Params *pInitParams) { + uint32_t i; + IoT_Error_t rc; + IoT_Client_Connect_Params default_options = IoT_Client_Connect_Params_initializer; + FUNC_ENTRY; + + if(NULL == pClient || NULL == pInitParams || NULL == pInitParams->pHostURL || 0 == pInitParams->port || + NULL == pInitParams->pRootCALocation || NULL == pInitParams->pDevicePrivateKeyLocation || + NULL == pInitParams->pDeviceCertLocation) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + for(i = 0; i < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; ++i) { + pClient->clientData.messageHandlers[i].topicName = NULL; + pClient->clientData.messageHandlers[i].pApplicationHandler = NULL; + pClient->clientData.messageHandlers[i].pApplicationHandlerData = NULL; + pClient->clientData.messageHandlers[i].qos = QOS0; + } + + pClient->clientData.packetTimeoutMs = pInitParams->mqttPacketTimeout_ms; + pClient->clientData.commandTimeoutMs = pInitParams->mqttCommandTimeout_ms; + pClient->clientData.writeBufSize = AWS_IOT_MQTT_TX_BUF_LEN; + pClient->clientData.readBufSize = AWS_IOT_MQTT_RX_BUF_LEN; + pClient->clientData.counterNetworkDisconnected = 0; + pClient->clientData.disconnectHandler = pInitParams->disconnectHandler; + pClient->clientData.disconnectHandlerData = pInitParams->disconnectHandlerData; + pClient->clientData.nextPacketId = 1; + + pClient->clientData.packetTimeoutMs = pInitParams->mqttPacketTimeout_ms; + /* Initialize default connection options */ + rc = aws_iot_mqtt_set_connect_params(pClient, &default_options); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + pClient->clientData.isBlockOnThreadLockEnabled = pInitParams->isBlockOnThreadLockEnabled; + rc = aws_iot_thread_mutex_init(&(pClient->clientData.state_change_mutex)); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + rc = aws_iot_thread_mutex_init(&(pClient->clientData.tls_read_mutex)); + if(AWS_SUCCESS != rc) { + (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex)); + FUNC_EXIT_RC(rc); + } + rc = aws_iot_thread_mutex_init(&(pClient->clientData.tls_write_mutex)); + if(AWS_SUCCESS != rc) { + (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_read_mutex)); + (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex)); + FUNC_EXIT_RC(rc); + } +#endif + + pClient->clientStatus.isPingOutstanding = 0; + pClient->clientStatus.isAutoReconnectEnabled = pInitParams->enableAutoReconnect; + + rc = iot_tls_init(&(pClient->networkStack), pInitParams->pRootCALocation, pInitParams->pDeviceCertLocation, + pInitParams->pDevicePrivateKeyLocation, pInitParams->pHostURL, pInitParams->port, + pInitParams->tlsHandshakeTimeout_ms, pInitParams->isSSLHostnameVerify); + + if(AWS_SUCCESS != rc) { + #ifdef _ENABLE_THREAD_SUPPORT_ + (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_read_mutex)); + (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.state_change_mutex)); + (void)aws_iot_thread_mutex_destroy(&(pClient->clientData.tls_write_mutex)); + #endif + pClient->clientStatus.clientState = CLIENT_STATE_INVALID; + FUNC_EXIT_RC(rc); + } + + init_timer(&(pClient->pingTimer)); + init_timer(&(pClient->reconnectDelayTimer)); + + pClient->clientStatus.clientState = CLIENT_STATE_INITIALIZED; + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +uint16_t aws_iot_mqtt_get_next_packet_id(AWS_IoT_Client *pClient) { + return pClient->clientData.nextPacketId = (uint16_t) ((MAX_PACKET_ID == pClient->clientData.nextPacketId) ? 1 : ( + pClient->clientData.nextPacketId + 1)); +} + +bool aws_iot_mqtt_is_client_connected(AWS_IoT_Client *pClient) { + bool isConnected; + + FUNC_ENTRY; + + if(NULL == pClient) { + IOT_WARN(" Client is null! "); + FUNC_EXIT_RC(false); + } + + switch(pClient->clientStatus.clientState) { + case CLIENT_STATE_INVALID: + case CLIENT_STATE_INITIALIZED: + case CLIENT_STATE_CONNECTING: + isConnected = false; + break; + case CLIENT_STATE_CONNECTED_IDLE: + case CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN: + isConnected = true; + break; + case CLIENT_STATE_DISCONNECTING: + case CLIENT_STATE_DISCONNECTED_ERROR: + case CLIENT_STATE_DISCONNECTED_MANUALLY: + case CLIENT_STATE_PENDING_RECONNECT: + default: + isConnected = false; + break; + } + + FUNC_EXIT_RC(isConnected); +} + +bool aws_iot_is_autoreconnect_enabled(AWS_IoT_Client *pClient) { + FUNC_ENTRY; + if(NULL == pClient) { + IOT_WARN(" Client is null! "); + FUNC_EXIT_RC(false); + } + + FUNC_EXIT_RC(pClient->clientStatus.isAutoReconnectEnabled); +} + +IoT_Error_t aws_iot_mqtt_autoreconnect_set_status(AWS_IoT_Client *pClient, bool newStatus) { + FUNC_ENTRY; + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + pClient->clientStatus.isAutoReconnectEnabled = newStatus; + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_set_disconnect_handler(AWS_IoT_Client *pClient, iot_disconnect_handler pDisconnectHandler, + void *pDisconnectHandlerData) { + FUNC_ENTRY; + if(NULL == pClient || NULL == pDisconnectHandler) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + pClient->clientData.disconnectHandler = pDisconnectHandler; + pClient->clientData.disconnectHandlerData = pDisconnectHandlerData; + FUNC_EXIT_RC(AWS_SUCCESS); +} + +uint32_t aws_iot_mqtt_get_network_disconnected_count(AWS_IoT_Client *pClient) { + return pClient->clientData.counterNetworkDisconnected; +} + +void aws_iot_mqtt_reset_network_disconnected_count(AWS_IoT_Client *pClient) { + pClient->clientData.counterNetworkDisconnected = 0; +} + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_mqtt_client_common_internal.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,691 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Sergio R. Caprile - non-blocking packet read functions for stream transport + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_common_internal.c + * @brief MQTT client internal API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <aws_iot_mqtt_client.h> +#include "aws_iot_mqtt_client_common_internal.h" + +/* Max length of packet header */ +#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 + +/** + * Encodes the message length according to the MQTT algorithm + * @param buf the buffer into which the encoded data is written + * @param length the length to be encoded + * @return the number of bytes written to buffer + */ +size_t aws_iot_mqtt_internal_write_len_to_buffer(unsigned char *buf, uint32_t length) { + size_t outLen = 0; + unsigned char encodedByte; + + FUNC_ENTRY; + do { + encodedByte = (unsigned char) (length % 128); + length /= 128; + /* if there are more digits to encode, set the top bit of this digit */ + if(length > 0) { + encodedByte |= 0x80; + } + buf[outLen++] = encodedByte; + } while(length > 0); + + FUNC_EXIT_RC(outLen); +} + +/** + * Decodes the message length according to the MQTT algorithm + * @param the buffer containing the message + * @param value the decoded length returned + * @return the number of bytes read from the socket + */ +IoT_Error_t aws_iot_mqtt_internal_decode_remaining_length_from_buffer(unsigned char *buf, uint32_t *decodedLen, + uint32_t *readBytesLen) { + unsigned char encodedByte; + uint32_t multiplier, len; + FUNC_ENTRY; + + multiplier = 1; + len = 0; + *decodedLen = 0; + + do { + if(++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) { + /* bad data */ + FUNC_EXIT_RC(MQTT_DECODE_REMAINING_LENGTH_ERROR); + } + encodedByte = *buf; + buf++; + *decodedLen += (encodedByte & 127) * multiplier; + multiplier *= 128; + } while((encodedByte & 128) != 0); + + *readBytesLen = len; + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +uint32_t aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(uint32_t rem_len) { + rem_len += 1; /* header byte */ + /* now remaining_length field (MQTT 3.1.1 - 2.2.3)*/ + if(rem_len < 128) { + rem_len += 1; + } else if(rem_len < 16384) { + rem_len += 2; + } else if(rem_len < 2097152) { + rem_len += 3; + } else { + rem_len += 4; + } + return rem_len; +} + +/** + * Calculates uint16 packet id from two bytes read from the input buffer + * Checks Endianness at runtime + * + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the value calculated + */ +uint16_t aws_iot_mqtt_internal_read_uint16_t(unsigned char **pptr) { + unsigned char *ptr = *pptr; + uint16_t len = 0; + uint8_t firstByte = (uint8_t) (*ptr); + uint8_t secondByte = (uint8_t) (*(ptr + 1)); + len = (uint16_t) (secondByte + (256 * firstByte)); + + *pptr += 2; + return len; +} + +/** + * Writes an integer as 2 bytes to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param anInt the integer to write + */ +void aws_iot_mqtt_internal_write_uint_16(unsigned char **pptr, uint16_t anInt) { + **pptr = (unsigned char) (anInt / 256); + (*pptr)++; + **pptr = (unsigned char) (anInt % 256); + (*pptr)++; +} + +/** + * Reads one character from the input buffer. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the character read + */ +unsigned char aws_iot_mqtt_internal_read_char(unsigned char **pptr) { + unsigned char c = **pptr; + (*pptr)++; + return c; +} + +/** + * Writes one character to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param c the character to write + */ +void aws_iot_mqtt_internal_write_char(unsigned char **pptr, unsigned char c) { + **pptr = c; + (*pptr)++; +} + +void aws_iot_mqtt_internal_write_utf8_string(unsigned char **pptr, const char *string, uint16_t stringLen) { + /* Nothing that calls this function will have a stringLen with a size larger than 2 bytes (MQTT 3.1.1 - 1.5.3) */ + aws_iot_mqtt_internal_write_uint_16(pptr, stringLen); + if(stringLen > 0) { + memcpy(*pptr, string, stringLen); + *pptr += stringLen; + } +} + +/** + * Initialize the MQTTHeader structure. Used to ensure that Header bits are + * always initialized using the proper mappings. No Endianness issues here since + * the individual fields are all less than a byte. Also generates no warnings since + * all fields are initialized using hex constants + */ +IoT_Error_t aws_iot_mqtt_internal_init_header(MQTTHeader *pHeader, MessageTypes message_type, + QoS qos, uint8_t dup, uint8_t retained) { + FUNC_ENTRY; + + if(NULL == pHeader) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* Set all bits to zero */ + pHeader->byte = 0; + uint8_t type = 0; + switch(message_type) { + case UNKNOWN: + /* Should never happen */ + return FAILURE; + case CONNECT: + type = 0x01; + break; + case CONNACK: + type = 0x02; + break; + case PUBLISH: + type = 0x03; + break; + case PUBACK: + type = 0x04; + break; + case PUBREC: + type = 0x05; + break; + case PUBREL: + type = 0x06; + break; + case PUBCOMP: + type = 0x07; + break; + case SUBSCRIBE: + type = 0x08; + break; + case SUBACK: + type = 0x09; + break; + case UNSUBSCRIBE: + type = 0x0A; + break; + case UNSUBACK: + type = 0x0B; + break; + case PINGREQ: + type = 0x0C; + break; + case PINGRESP: + type = 0x0D; + break; + case DISCONNECT: + type = 0x0E; + break; + default: + /* Should never happen */ + FUNC_EXIT_RC(FAILURE); + } + + pHeader->byte = type << 4; + pHeader->byte |= dup << 3; + + switch(qos) { + case QOS0: + break; + case QOS1: + pHeader->byte |= 1 << 1; + break; + default: + /* Using QOS0 as default */ + break; + } + + pHeader->byte |= (1 == retained) ? 0x01 : 0x00; + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_internal_send_packet(AWS_IoT_Client *pClient, size_t length, awsTimer *pTimer) { + + size_t sentLen, sent; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pTimer) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(length >= pClient->clientData.writeBufSize) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + rc = aws_iot_mqtt_client_lock_mutex(pClient, &(pClient->clientData.tls_write_mutex)); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } +#endif + + sentLen = 0; + sent = 0; + + while(sent < length && !has_timer_expired(pTimer)) { + rc = pClient->networkStack.write(&(pClient->networkStack), + &pClient->clientData.writeBuf[sent], + (length - sent), + pTimer, + &sentLen); + if(AWS_SUCCESS != rc) { + /* there was an error writing the data */ + break; + } + sent += sentLen; + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + rc = aws_iot_mqtt_client_unlock_mutex(pClient, &(pClient->clientData.tls_write_mutex)); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } +#endif + + if(sent == length) { + /* record the fact that we have successfully sent the packet */ + //countdown_sec(&c->pingTimer, c->clientData.keepAliveInterval); + FUNC_EXIT_RC(AWS_SUCCESS); + } + + FUNC_EXIT_RC(rc) +} + +static IoT_Error_t _aws_iot_mqtt_internal_decode_packet_remaining_len(AWS_IoT_Client *pClient, + size_t *rem_len, awsTimer *pTimer) { + unsigned char encodedByte; + size_t multiplier, len; + IoT_Error_t rc; + + FUNC_ENTRY; + + multiplier = 1; + len = 0; + *rem_len = 0; + + do { + if(++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) { + /* bad data */ + FUNC_EXIT_RC(MQTT_DECODE_REMAINING_LENGTH_ERROR); + } + + rc = pClient->networkStack.read(&(pClient->networkStack), &encodedByte, 1, pTimer, &len); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + *rem_len += ((encodedByte & 127) * multiplier); + multiplier *= 128; + } while((encodedByte & 128) != 0); + + FUNC_EXIT_RC(rc); +} + +static IoT_Error_t _aws_iot_mqtt_internal_read_packet(AWS_IoT_Client *pClient, awsTimer *pTimer, uint8_t *pPacketType) { + size_t len, rem_len, total_bytes_read, bytes_to_be_read, read_len; + IoT_Error_t rc; + MQTTHeader header = {0}; + awsTimer packetTimer; + init_timer(&packetTimer); + countdown_ms(&packetTimer, pClient->clientData.packetTimeoutMs); + + rem_len = 0; + total_bytes_read = 0; + bytes_to_be_read = 0; + read_len = 0; + + rc = pClient->networkStack.read(&(pClient->networkStack), pClient->clientData.readBuf, 1, pTimer, &read_len); +//printf("JMF:%s:%d read %d from networkStack.read\n",__FILE__,__LINE__,rc); + + /* 1. read the header byte. This has the packet type in it */ + if(NETWORK_SSL_NOTHING_TO_READ == rc) { + return MQTT_NOTHING_TO_READ; + } else if(AWS_SUCCESS != rc) { + return rc; + } + + len = 1; + + /* Use the constant packet receive timeout, instead of the variable (remaining) pTimer time, to + * determine packet receiving timeout. This is done so we don't prematurely time out packet receiving + * if the remaining time in pTimer is too short. + */ + pTimer = &packetTimer; + + /* 2. read the remaining length. This is variable in itself */ + rc = _aws_iot_mqtt_internal_decode_packet_remaining_len(pClient, &rem_len, pTimer); + if(AWS_SUCCESS != rc) { + return rc; + } + + /* if the buffer is too short then the message will be dropped silently */ + if(rem_len >= pClient->clientData.readBufSize) { + bytes_to_be_read = pClient->clientData.readBufSize; + do { + rc = pClient->networkStack.read(&(pClient->networkStack), pClient->clientData.readBuf, bytes_to_be_read, + pTimer, &read_len); + if(AWS_SUCCESS == rc) { + total_bytes_read += read_len; + if((rem_len - total_bytes_read) >= pClient->clientData.readBufSize) { + bytes_to_be_read = pClient->clientData.readBufSize; + } else { + bytes_to_be_read = rem_len - total_bytes_read; + } + } + } while(total_bytes_read < rem_len && AWS_SUCCESS == rc); + return MQTT_RX_BUFFER_TOO_SHORT_ERROR; + } + + /* put the original remaining length into the read buffer */ + len += aws_iot_mqtt_internal_write_len_to_buffer(pClient->clientData.readBuf + 1, (uint32_t) rem_len); + + /* 3. read the rest of the buffer using a callback to supply the rest of the data */ + if(rem_len > 0) { + rc = pClient->networkStack.read(&(pClient->networkStack), pClient->clientData.readBuf + len, rem_len, pTimer, + &read_len); + if(AWS_SUCCESS != rc || read_len != rem_len) { + return FAILURE; + } + } + + header.byte = pClient->clientData.readBuf[0]; + *pPacketType = MQTT_HEADER_FIELD_TYPE(header.byte); + + FUNC_EXIT_RC(rc); +} + +// assume topic filter and name is in correct format +// # can only be at end +// + and # can only be next to separator +static bool _aws_iot_mqtt_internal_is_topic_matched(char *pTopicFilter, char *pTopicName, uint16_t topicNameLen) { + + char *curf, *curn, *curn_end; + + if(NULL == pTopicFilter || NULL == pTopicName) { + return false; + } + + curf = pTopicFilter; + curn = pTopicName; + curn_end = curn + topicNameLen; + + while(*curf && (curn < curn_end)) { + if(*curn == '/' && *curf != '/') { + break; + } + if(*curf != '+' && *curf != '#' && *curf != *curn) { + break; + } + if(*curf == '+') { + /* skip until we meet the next separator, or end of string */ + char *nextpos = curn + 1; + while(nextpos < curn_end && *nextpos != '/') + nextpos = ++curn + 1; + } else if(*curf == '#') { + /* skip until end of string */ + curn = curn_end - 1; + } + + curf++; + curn++; + }; + + return (curn == curn_end) && (*curf == '\0'); +} + +static IoT_Error_t _aws_iot_mqtt_internal_deliver_message(AWS_IoT_Client *pClient, char *pTopicName, + uint16_t topicNameLen, + IoT_Publish_Message_Params *pMessageParams) { + uint32_t itr; + IoT_Error_t rc; + ClientState clientState; + + FUNC_ENTRY; + + if(NULL == pTopicName) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* This function can be called from all MQTT APIs + * But while callback return is in progress, Yield should not be called. + * The state for CB_RETURN accomplishes that, as yield cannot be called while in that state */ + clientState = aws_iot_mqtt_get_client_state(pClient); + aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN); + + /* Find the right message handler - indexed by topic */ + for(itr = 0; itr < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; ++itr) { + if(NULL != pClient->clientData.messageHandlers[itr].topicName) { + if(((topicNameLen == pClient->clientData.messageHandlers[itr].topicNameLen) + && + (strncmp(pTopicName, (char *) pClient->clientData.messageHandlers[itr].topicName, topicNameLen) == 0)) + || _aws_iot_mqtt_internal_is_topic_matched((char *) pClient->clientData.messageHandlers[itr].topicName, + pTopicName, topicNameLen)) { + if(NULL != pClient->clientData.messageHandlers[itr].pApplicationHandler) { + pClient->clientData.messageHandlers[itr].pApplicationHandler(pClient, pTopicName, topicNameLen, + pMessageParams, + pClient->clientData.messageHandlers[itr].pApplicationHandlerData); + } + } + } + } + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN, clientState); + + FUNC_EXIT_RC(rc); +} + +static IoT_Error_t _aws_iot_mqtt_internal_handle_publish(AWS_IoT_Client *pClient, awsTimer *pTimer) { + char *topicName; + uint16_t topicNameLen; + uint32_t len; + IoT_Error_t rc; + IoT_Publish_Message_Params msg; + + FUNC_ENTRY; + + topicName = NULL; + topicNameLen = 0; + len = 0; + + rc = aws_iot_mqtt_internal_deserialize_publish(&msg.isDup, &msg.qos, &msg.isRetained, + &msg.id, &topicName, &topicNameLen, + (unsigned char **) &msg.payload, &msg.payloadLen, + pClient->clientData.readBuf, + pClient->clientData.readBufSize); + + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = _aws_iot_mqtt_internal_deliver_message(pClient, topicName, topicNameLen, &msg); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + if(QOS0 == msg.qos) { + /* No further processing required for QoS0 */ + FUNC_EXIT_RC(AWS_SUCCESS); + } + + /* Message assumed to be QoS1 since we do not support QoS2 at this time */ + rc = aws_iot_mqtt_internal_serialize_ack(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, + PUBACK, 0, msg.id, &len); + + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = aws_iot_mqtt_internal_send_packet(pClient, len, pTimer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_internal_cycle_read(AWS_IoT_Client *pClient, awsTimer *pTimer, uint8_t *pPacketType) { + IoT_Error_t rc; +//printf("JMF! in aws_iot_mqtt_internal_cycle_read\n"); +#ifdef _ENABLE_THREAD_SUPPORT_ + IoT_Error_t threadRc; +#endif + + if(NULL == pClient || NULL == pTimer) { + return NULL_VALUE_ERROR; + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + threadRc = aws_iot_mqtt_client_lock_mutex(pClient, &(pClient->clientData.tls_read_mutex)); + if(AWS_SUCCESS != threadRc) { + FUNC_EXIT_RC(threadRc); + } +#endif + + /* read the socket, see what work is due */ + rc = _aws_iot_mqtt_internal_read_packet(pClient, pTimer, pPacketType); +//printf("JMF:%s:%d rc=%d\n",__FILE__,__LINE__,rc); + +#ifdef _ENABLE_THREAD_SUPPORT_ + threadRc = aws_iot_mqtt_client_unlock_mutex(pClient, &(pClient->clientData.tls_read_mutex)); + if(AWS_SUCCESS != threadRc && (MQTT_NOTHING_TO_READ == rc || AWS_SUCCESS == rc)) { + return threadRc; + } +#endif + + if(MQTT_NOTHING_TO_READ == rc) { + /* Nothing to read, not a cycle failure */ +//printf("JMF:%s:%d rc=%d\n",__FILE__,__LINE__,rc); + return AWS_SUCCESS; + } else if(AWS_SUCCESS != rc) { +//printf("JMF:%s:%d rc=%d\n",__FILE__,__LINE__,rc); + return rc; + } +//printf("JMF: cycle_read got a char, switch on %d\n",*pPacketType); + + switch(*pPacketType) { + case CONNACK: + case PUBACK: + case SUBACK: + case UNSUBACK: + /* SDK is blocking, these responses will be forwarded to calling function to process */ + break; + case PUBLISH: { + rc = _aws_iot_mqtt_internal_handle_publish(pClient, pTimer); + break; + } + case PUBREC: + case PUBCOMP: + /* QoS2 not supported at this time */ + break; + case PINGRESP: { + pClient->clientStatus.isPingOutstanding = 0; + countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval); + break; + } + default: { + /* Either unknown packet type or Failure occurred + * Should not happen */ + rc = MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR; + break; + } + } + +//printf("JMF:%s:%d EXITING rc=%d\n",__FILE__,__LINE__,rc); + return rc; +} + +/* only used in single-threaded mode where one command at a time is in process */ +IoT_Error_t aws_iot_mqtt_internal_wait_for_read(AWS_IoT_Client *pClient, uint8_t packetType, awsTimer *pTimer) { + IoT_Error_t rc; + uint8_t read_packet_type; + + FUNC_ENTRY; + if(NULL == pClient || NULL == pTimer) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + read_packet_type = 0; + do { + if(has_timer_expired(pTimer)) { + /* we timed out */ + rc = MQTT_REQUEST_TIMEOUT_ERROR; + break; + } + rc = aws_iot_mqtt_internal_cycle_read(pClient, pTimer, &read_packet_type); + } while(((AWS_SUCCESS == rc) || (MQTT_NOTHING_TO_READ == rc)) && (read_packet_type != packetType)); + + /* If rc is AWS_SUCCESS, we have received the expected + * MQTT packet. Otherwise rc tells the error. */ + FUNC_EXIT_RC(rc); +} + +/** + * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @param packettype the message type + * @param serialized length + * @return IoT_Error_t indicating function execution status + */ +IoT_Error_t aws_iot_mqtt_internal_serialize_zero(unsigned char *pTxBuf, size_t txBufLen, MessageTypes packetType, + size_t *pSerializedLength) { + unsigned char *ptr; + IoT_Error_t rc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + if(NULL == pTxBuf || NULL == pSerializedLength) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* Buffer should have at least 2 bytes for the header */ + if(4 > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + ptr = pTxBuf; + + rc = aws_iot_mqtt_internal_init_header(&header, packetType, QOS0, 0, 0); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* write header */ + aws_iot_mqtt_internal_write_char(&ptr, header.byte); + + /* write remaining length */ + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, 0); + *pSerializedLength = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_mqtt_client_connect.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,626 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_connect.c + * @brief MQTT client connect API definition and related functions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> + +#include <aws_iot_mqtt_client.h> +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_mqtt_client_common_internal.h" + +typedef union { + uint8_t all; /**< all connect flags */ +#if defined(REVERSED) + struct + { + unsigned int username : 1; /**< 3.1 user name */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int will : 1; /**< will flag */ + unsigned int cleansession : 1; /**< clean session flag */ + unsigned int : 1; /**< unused */ + } bits; +#else + struct { + unsigned int : 1; + /**< unused */ + unsigned int cleansession : 1; + /**< cleansession flag */ + unsigned int will : 1; + /**< will flag */ + unsigned int willQoS : 2; + /**< will QoS value */ + unsigned int willRetain : 1; + /**< will retain setting */ + unsigned int password : 1; + /**< 3.1 password */ + unsigned int username : 1; /**< 3.1 user name */ + } bits; +#endif +} MQTT_Connect_Header_Flags; +/**< connect flags byte */ + +typedef union { + uint8_t all; /**< all connack flags */ +#if defined(REVERSED) + struct + { + unsigned int sessionpresent : 1; /**< session present flag */ + unsigned int : 7; /**< unused */ + } bits; +#else + struct { + unsigned int : 7; + /**< unused */ + unsigned int sessionpresent : 1; /**< session present flag */ + } bits; +#endif +} MQTT_Connack_Header_Flags; +/**< connack flags byte */ + +typedef enum { + CONNACK_CONNECTION_ACCEPTED = 0, + CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR = 1, + CONNACK_IDENTIFIER_REJECTED_ERROR = 2, + CONNACK_SERVER_UNAVAILABLE_ERROR = 3, + CONNACK_BAD_USERDATA_ERROR = 4, + CONNACK_NOT_AUTHORIZED_ERROR = 5 +} MQTT_Connack_Return_Codes; /**< Connect request response codes from server */ + + +/** + * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. + * @param options the options to be used to build the connect packet + * @param the length of buffer needed to contain the serialized version of the packet + * @return IoT_Error_t indicating function execution status + */ +static uint32_t _aws_iot_get_connect_packet_length(IoT_Client_Connect_Params *pConnectParams) { + uint32_t len; + /* Enable when adding further MQTT versions */ + /*size_t len = 0; + switch(pConnectParams->MQTTVersion) { + case MQTT_3_1_1: + len = 10; + break; + }*/ + FUNC_ENTRY; + + len = 10; // Len = 10 for MQTT_3_1_1 + len = len + pConnectParams->clientIDLen + 2; + + if(pConnectParams->isWillMsgPresent) { + len = len + pConnectParams->will.topicNameLen + 2 + pConnectParams->will.msgLen + 2; + } + + if(NULL != pConnectParams->pUsername) { + len = len + pConnectParams->usernameLen + 2; + } + + if(NULL != pConnectParams->pPassword) { + len = len + pConnectParams->passwordLen + 2; + } + + FUNC_EXIT_RC((int)len); +} + +/** + * Serializes the connect options into the buffer. + * @param buf the buffer into which the packet will be serialized + * @param len the length in bytes of the supplied buffer + * @param options the options to be used to build the connect packet + * @param serialized length + * @return IoT_Error_t indicating function execution status + */ +static IoT_Error_t _aws_iot_mqtt_serialize_connect(unsigned char *pTxBuf, size_t txBufLen, IoT_Client_Connect_Params *pConnectParams, + size_t *pSerializedLen) +{ + unsigned char *ptr; + uint32_t len; + IoT_Error_t rc; + MQTTHeader header = {0}; + MQTT_Connect_Header_Flags flags = {0}; + + FUNC_ENTRY; + + if(NULL == pTxBuf || NULL == pConnectParams || NULL == pSerializedLen || + (NULL == pConnectParams->pClientID && 0 != pConnectParams->clientIDLen) || + (NULL != pConnectParams->pClientID && 0 == pConnectParams->clientIDLen)) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* Check needed here before we start writing to the Tx buffer */ + switch(pConnectParams->MQTTVersion) { + case MQTT_3_1_1: + break; + default: + return MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR; + } + + ptr = pTxBuf; + len = _aws_iot_get_connect_packet_length(pConnectParams); + if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(len) > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + rc = aws_iot_mqtt_internal_init_header(&header, CONNECT, QOS0, 0, 0); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + aws_iot_mqtt_internal_write_char(&ptr, header.byte); /* write header */ + + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, len); /* write remaining length */ + + // Enable if adding support for more versions + //if(MQTT_3_1_1 == pConnectParams->MQTTVersion) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, "MQTT", 4); + aws_iot_mqtt_internal_write_char(&ptr, (unsigned char) pConnectParams->MQTTVersion); + //} + + flags.all = 0; + if (pConnectParams->isCleanSession) + { + flags.all |= 1 << 1; + } + + if (pConnectParams->isWillMsgPresent) + { + flags.all |= 1 << 2; + flags.all |= pConnectParams->will.qos << 3; + flags.all |= pConnectParams->will.isRetained << 5; + } + + if(pConnectParams->pPassword) { + flags.all |= 1 << 6; + } + + if(pConnectParams->pUsername) { + flags.all |= 1 << 7; + } + + aws_iot_mqtt_internal_write_char(&ptr, flags.all); + aws_iot_mqtt_internal_write_uint_16(&ptr, pConnectParams->keepAliveIntervalInSec); + + /* If the code have passed the check for incorrect values above, no client id was passed as argument */ + if(NULL == pConnectParams->pClientID) { + aws_iot_mqtt_internal_write_uint_16(&ptr, 0); + } else { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->pClientID, pConnectParams->clientIDLen); + } + + if(pConnectParams->isWillMsgPresent) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->will.pTopicName, + pConnectParams->will.topicNameLen); + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->will.pMessage, pConnectParams->will.msgLen); + } + + if(pConnectParams->pUsername) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->pUsername, pConnectParams->usernameLen); + } + + if(pConnectParams->pPassword) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->pPassword, pConnectParams->passwordLen); + } + + *pSerializedLen = (size_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * Deserializes the supplied (wire) buffer into connack data - return code + * @param sessionPresent the session present flag returned (only for MQTT 3.1.1) + * @param connack_rc returned integer value of the connack return code + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return IoT_Error_t indicating function execution status + */ +static IoT_Error_t _aws_iot_mqtt_deserialize_connack(unsigned char *pSessionPresent, IoT_Error_t *pConnackRc, + unsigned char *pRxBuf, size_t rxBufLen) { + unsigned char *curdata, *enddata; + unsigned char connack_rc_char; + uint32_t decodedLen, readBytesLen; + IoT_Error_t rc; + MQTT_Connack_Header_Flags flags = {0}; + MQTTHeader header = {0}; + + FUNC_ENTRY; + + if(NULL == pSessionPresent || NULL == pConnackRc || NULL == pRxBuf) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* CONNACK header size is fixed at two bytes for fixed and 2 bytes for variable, + * using that as minimum size + * MQTT v3.1.1 Specification 3.2.1 */ + if(4 > rxBufLen) { + FUNC_EXIT_RC(MQTT_RX_BUFFER_TOO_SHORT_ERROR); + } + + curdata = pRxBuf; + enddata = NULL; + decodedLen = 0; + readBytesLen = 0; + + header.byte = aws_iot_mqtt_internal_read_char(&curdata); + if(CONNACK != MQTT_HEADER_FIELD_TYPE(header.byte)) { + FUNC_EXIT_RC(FAILURE); + } + + /* read remaining length */ + rc = aws_iot_mqtt_internal_decode_remaining_length_from_buffer(curdata, &decodedLen, &readBytesLen); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* CONNACK remaining length should always be 2 as per MQTT 3.1.1 spec */ + curdata += (readBytesLen); + enddata = curdata + decodedLen; + if(2 != (enddata - curdata)) { + FUNC_EXIT_RC(MQTT_DECODE_REMAINING_LENGTH_ERROR); + } + + flags.all = aws_iot_mqtt_internal_read_char(&curdata); + *pSessionPresent = flags.bits.sessionpresent; + connack_rc_char = aws_iot_mqtt_internal_read_char(&curdata); + switch(connack_rc_char) { + case CONNACK_CONNECTION_ACCEPTED: + *pConnackRc = MQTT_CONNACK_CONNECTION_ACCEPTED; + break; + case CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR: + *pConnackRc = MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR; + break; + case CONNACK_IDENTIFIER_REJECTED_ERROR: + *pConnackRc = MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR; + break; + case CONNACK_SERVER_UNAVAILABLE_ERROR: + *pConnackRc = MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR; + break; + case CONNACK_BAD_USERDATA_ERROR: + *pConnackRc = MQTT_CONNACK_BAD_USERDATA_ERROR; + break; + case CONNACK_NOT_AUTHORIZED_ERROR: + *pConnackRc = MQTT_CONNACK_NOT_AUTHORIZED_ERROR; + break; + default: + *pConnackRc = MQTT_CONNACK_UNKNOWN_ERROR; + break; + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * @brief Check if client state is valid for a connect request + * + * Called to check if client state is valid for a connect request + * @param pClient Reference to the IoT Client + * + * @return bool true = state is valid, false = not valid + */ +static bool _aws_iot_mqtt_is_client_state_valid_for_connect(ClientState clientState) { + bool isValid = false; + + switch(clientState) { + case CLIENT_STATE_INVALID: + isValid = false; + break; + case CLIENT_STATE_INITIALIZED: + isValid = true; + break; + case CLIENT_STATE_CONNECTING: + case CLIENT_STATE_CONNECTED_IDLE: + case CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN: + case CLIENT_STATE_DISCONNECTING: + isValid = false; + break; + case CLIENT_STATE_DISCONNECTED_ERROR: + case CLIENT_STATE_DISCONNECTED_MANUALLY: + case CLIENT_STATE_PENDING_RECONNECT: + isValid = true; + break; + default: + break; + } + + return isValid; +} + +/** + * @brief MQTT Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * This is the internal function which is called by the connect API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * @param pConnectParams Pointer to MQTT connection parameters + * + * @return An IoT Error Type defining successful/failed connection + */ +static IoT_Error_t _aws_iot_mqtt_internal_connect(AWS_IoT_Client *pClient, IoT_Client_Connect_Params *pConnectParams) { + awsTimer connect_timer; + IoT_Error_t connack_rc = FAILURE; + char sessionPresent = 0; + size_t len = 0; + IoT_Error_t rc = FAILURE; + + FUNC_ENTRY; + + if(NULL != pConnectParams) { + /* override default options if new options were supplied */ + rc = aws_iot_mqtt_set_connect_params(pClient, pConnectParams); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(MQTT_CONNECTION_ERROR); + } + } + + rc = pClient->networkStack.connect(&(pClient->networkStack), NULL); + if(AWS_SUCCESS != rc) { + /* TLS Connect failed, return error */ + FUNC_EXIT_RC(rc); + } + + init_timer(&connect_timer); + countdown_ms(&connect_timer, pClient->clientData.commandTimeoutMs); + + pClient->clientData.keepAliveInterval = pClient->clientData.options.keepAliveIntervalInSec; + rc = _aws_iot_mqtt_serialize_connect(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, + &(pClient->clientData.options), &len); + if(AWS_SUCCESS != rc || 0 >= len) { + FUNC_EXIT_RC(rc); + } + + /* send the connect packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, len, &connect_timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* this will be a blocking call, wait for the CONNACK */ + rc = aws_iot_mqtt_internal_wait_for_read(pClient, CONNACK, &connect_timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Received CONNACK, check the return code */ + rc = _aws_iot_mqtt_deserialize_connack((unsigned char *) &sessionPresent, &connack_rc, pClient->clientData.readBuf, + pClient->clientData.readBufSize); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + if(MQTT_CONNACK_CONNECTION_ACCEPTED != connack_rc) { + FUNC_EXIT_RC(connack_rc); + } + + pClient->clientStatus.isPingOutstanding = false; + countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * @brief MQTT Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * This is the outer function which does the validations and calls the internal connect above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * @param pConnectParams Pointer to MQTT connection parameters + * + * @return An IoT Error Type defining successful/failed connection + */ +IoT_Error_t aws_iot_mqtt_connect(AWS_IoT_Client *pClient, IoT_Client_Connect_Params *pConnectParams) { + IoT_Error_t rc, disconRc; + ClientState clientState; + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + clientState = aws_iot_mqtt_get_client_state(pClient); + + if(false == _aws_iot_mqtt_is_client_state_valid_for_connect(clientState)) { + /* Don't send connect packet again if we are already connected + * or in the process of connecting/disconnecting */ + FUNC_EXIT_RC(NETWORK_ALREADY_CONNECTED_ERROR); + } + + aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTING); + + rc = _aws_iot_mqtt_internal_connect(pClient, pConnectParams); + + if(AWS_SUCCESS != rc) { + pClient->networkStack.disconnect(&(pClient->networkStack)); + disconRc = pClient->networkStack.destroy(&(pClient->networkStack)); + if (AWS_SUCCESS != disconRc) { + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTING, CLIENT_STATE_DISCONNECTED_ERROR); + } + else{ + aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTING, CLIENT_STATE_CONNECTED_IDLE); + } + FUNC_EXIT_RC(rc); +} + +/** + * @brief Disconnect an MQTT Connection + * + * Called to send a disconnect message to the broker. + * This is the internal function which is called by the disconnect API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed send of the disconnect control packet. + */ +IoT_Error_t _aws_iot_mqtt_internal_disconnect(AWS_IoT_Client *pClient) { + /* We might wait for incomplete incoming publishes to complete */ + awsTimer timer; + size_t serialized_len = 0; + IoT_Error_t rc; + + FUNC_ENTRY; + + rc = aws_iot_mqtt_internal_serialize_zero(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, + DISCONNECT, + &serialized_len); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + /* send the disconnect packet */ + if(serialized_len > 0) { + (void)aws_iot_mqtt_internal_send_packet(pClient, serialized_len, &timer); + } + + /* Clean network stack */ + pClient->networkStack.disconnect(&(pClient->networkStack)); + rc = pClient->networkStack.destroy(&(pClient->networkStack)); + if(0 != rc) { + /* TLS Destroy failed, return error */ + FUNC_EXIT_RC(FAILURE); + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * @brief Disconnect an MQTT Connection + * + * Called to send a disconnect message to the broker. + * This is the outer function which does the validations and calls the internal disconnect above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed send of the disconnect control packet. + */ +IoT_Error_t aws_iot_mqtt_disconnect(AWS_IoT_Client *pClient) { + ClientState clientState; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + if(!aws_iot_mqtt_is_client_connected(pClient)) { + /* Network is already disconnected. Do nothing */ + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_DISCONNECTING); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = _aws_iot_mqtt_internal_disconnect(pClient); + + if(AWS_SUCCESS != rc) { + pClient->clientStatus.clientState = clientState; + } else { + /* If called from Keepalive, this gets set to CLIENT_STATE_DISCONNECTED_ERROR */ + pClient->clientStatus.clientState = CLIENT_STATE_DISCONNECTED_MANUALLY; + } + + FUNC_EXIT_RC(rc); +} + +/** + * @brief MQTT Manual Re-Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * using parameters from the last time a connection was attempted + * Use after disconnect to start the reconnect process manually + * Makes only one reconnect attempt. Sets the client state to + * pending reconnect in case of failure + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed connection + */ +IoT_Error_t aws_iot_mqtt_attempt_reconnect(AWS_IoT_Client *pClient) { + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(NETWORK_ALREADY_CONNECTED_ERROR); + } + + /* Ignoring return code. failures expected if network is disconnected */ + rc = aws_iot_mqtt_connect(pClient, NULL); + + /* If still disconnected handle disconnect */ + if(CLIENT_STATE_CONNECTED_IDLE != aws_iot_mqtt_get_client_state(pClient)) { + aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_DISCONNECTED_ERROR, CLIENT_STATE_PENDING_RECONNECT); + FUNC_EXIT_RC(NETWORK_ATTEMPTING_RECONNECT); + } + + rc = aws_iot_mqtt_resubscribe(pClient); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + FUNC_EXIT_RC(NETWORK_RECONNECTED); +} + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_mqtt_client_publish.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,428 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144 + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_publish.c + * @brief MQTT client publish API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_mqtt_client_common_internal.h" + +/** + * @param stringVar pointer to the String into which the data is to be read + * @param stringLen pointer to variable which has the length of the string + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param enddata pointer to the end of the data: do not read beyond + * @return AWS_SUCCESS if successful, FAILURE if not + */ +static IoT_Error_t _aws_iot_mqtt_read_string_with_len(char **stringVar, uint16_t *stringLen, + unsigned char **pptr, unsigned char *enddata) { + IoT_Error_t rc = FAILURE; + + FUNC_ENTRY; + /* the first two bytes are the length of the string */ + /* enough length to read the integer? */ + if(enddata - (*pptr) > 1) { + *stringLen = aws_iot_mqtt_internal_read_uint16_t(pptr); /* increments pptr to point past length */ + if(&(*pptr)[*stringLen] <= enddata) { + *stringVar = (char *) *pptr; + *pptr += *stringLen; + rc = AWS_SUCCESS; + } + } + + FUNC_EXIT_RC(rc); +} + +/** + * Serializes the supplied publish data into the supplied buffer, ready for sending + * @param pTxBuf the buffer into which the packet will be serialized + * @param txBufLen the length in bytes of the supplied buffer + * @param dup uint8_t - the MQTT dup flag + * @param qos QoS - the MQTT QoS value + * @param retained uint8_t - the MQTT retained flag + * @param packetId uint16_t - the MQTT packet identifier + * @param pTopicName char * - the MQTT topic in the publish + * @param topicNameLen uint16_t - the length of the Topic Name + * @param pPayload byte buffer - the MQTT publish payload + * @param payloadLen size_t - the length of the MQTT payload + * @param pSerializedLen uint32_t - pointer to the variable that stores serialized len + * + * @return An IoT Error Type defining successful/failed call + */ +static IoT_Error_t _aws_iot_mqtt_internal_serialize_publish(unsigned char *pTxBuf, size_t txBufLen, uint8_t dup, + QoS qos, uint8_t retained, uint16_t packetId, + const char *pTopicName, uint16_t topicNameLen, + const unsigned char *pPayload, size_t payloadLen, + uint32_t *pSerializedLen) { + unsigned char *ptr; + uint32_t rem_len; + IoT_Error_t rc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + if(NULL == pTxBuf || NULL == pPayload || NULL == pSerializedLen) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + ptr = pTxBuf; + rem_len = 0; + + rem_len += (uint32_t) (topicNameLen + payloadLen + 2); + if(qos > 0) { + rem_len += 2; /* packetId */ + } + if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(rem_len) > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + rc = aws_iot_mqtt_internal_init_header(&header, PUBLISH, qos, dup, retained); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + aws_iot_mqtt_internal_write_char(&ptr, header.byte); /* write header */ + + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, rem_len); /* write remaining length */; + + aws_iot_mqtt_internal_write_utf8_string(&ptr, pTopicName, topicNameLen); + + if(qos > 0) { + aws_iot_mqtt_internal_write_uint_16(&ptr, packetId); + } + + memcpy(ptr, pPayload, payloadLen); + ptr += payloadLen; + + *pSerializedLen = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * Serializes the ack packet into the supplied buffer. + * @param pTxBuf the buffer into which the packet will be serialized + * @param txBufLen the length in bytes of the supplied buffer + * @param msgType the MQTT packet type + * @param dup the MQTT dup flag + * @param packetId the MQTT packet identifier + * @param pSerializedLen uint32_t - pointer to the variable that stores serialized len + * + * @return An IoT Error Type defining successful/failed call + */ +IoT_Error_t aws_iot_mqtt_internal_serialize_ack(unsigned char *pTxBuf, size_t txBufLen, + MessageTypes msgType, uint8_t dup, uint16_t packetId, + uint32_t *pSerializedLen) { + unsigned char *ptr; + QoS requestQoS; + IoT_Error_t rc; + MQTTHeader header = {0}; + FUNC_ENTRY; + if(NULL == pTxBuf || pSerializedLen == NULL) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + ptr = pTxBuf; + + /* Minimum byte length required by ACK headers is + * 2 for fixed and 2 for variable part */ + if(4 > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + requestQoS = (PUBREL == msgType) ? QOS1 : QOS0; + rc = aws_iot_mqtt_internal_init_header(&header, msgType, requestQoS, dup, 0); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + aws_iot_mqtt_internal_write_char(&ptr, header.byte); /* write header */ + + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, 2); /* write remaining length */ + aws_iot_mqtt_internal_write_uint_16(&ptr, packetId); + *pSerializedLen = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * @brief Publish an MQTT message on a topic + * + * Called to publish an MQTT message on a topic. + * @note Call is blocking. In the case of a QoS 0 message the function returns + * after the message was successfully passed to the TLS layer. In the case of QoS 1 + * the function returns after the receipt of the PUBACK control packet. + * This is the internal function which is called by the publish API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * @param pParams Pointer to Publish Message parameters + * + * @return An IoT Error Type defining successful/failed publish + */ +static IoT_Error_t _aws_iot_mqtt_internal_publish(AWS_IoT_Client *pClient, const char *pTopicName, + uint16_t topicNameLen, IoT_Publish_Message_Params *pParams) { + awsTimer timer; + uint32_t len = 0; + uint16_t packet_id; + unsigned char dup, type; + IoT_Error_t rc; + + FUNC_ENTRY; + + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + if(QOS1 == pParams->qos) { + pParams->id = aws_iot_mqtt_get_next_packet_id(pClient); + } + + rc = _aws_iot_mqtt_internal_serialize_publish(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 0, + pParams->qos, pParams->isRetained, pParams->id, pTopicName, + topicNameLen, (unsigned char *) pParams->payload, + pParams->payloadLen, &len); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* send the publish packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, len, &timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Wait for ack if QoS1 */ + if(QOS1 == pParams->qos) { + rc = aws_iot_mqtt_internal_wait_for_read(pClient, PUBACK, &timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = aws_iot_mqtt_internal_deserialize_ack(&type, &dup, &packet_id, pClient->clientData.readBuf, + pClient->clientData.readBufSize); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * @brief Publish an MQTT message on a topic + * + * Called to publish an MQTT message on a topic. + * @note Call is blocking. In the case of a QoS 0 message the function returns + * after the message was successfully passed to the TLS layer. In the case of QoS 1 + * the function returns after the receipt of the PUBACK control packet. + * This is the outer function which does the validations and calls the internal publish above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * @param pParams Pointer to Publish Message parameters + * + * @return An IoT Error Type defining successful/failed publish + */ +IoT_Error_t aws_iot_mqtt_publish(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *pParams) { + IoT_Error_t rc, pubRc; + ClientState clientState; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pTopicName || 0 == topicNameLen || NULL == pParams) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + if(CLIENT_STATE_CONNECTED_IDLE != clientState && CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN != clientState) { + FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + pubRc = _aws_iot_mqtt_internal_publish(pClient, pTopicName, topicNameLen, pParams); + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS, clientState); + if(AWS_SUCCESS == pubRc && AWS_SUCCESS != rc) { + pubRc = rc; + } + + FUNC_EXIT_RC(pubRc); +} + +/** + * Deserializes the supplied (wire) buffer into publish data + * @param dup returned uint8_t - the MQTT dup flag + * @param qos returned QoS type - the MQTT QoS value + * @param retained returned uint8_t - the MQTT retained flag + * @param pPacketId returned uint16_t - the MQTT packet identifier + * @param pTopicName returned String - the MQTT topic in the publish + * @param topicNameLen returned uint16_t - the length of the MQTT topic in the publish + * @param payload returned byte buffer - the MQTT publish payload + * @param payloadlen returned size_t - the length of the MQTT payload + * @param pRxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param rxBufLen the length in bytes of the data in the supplied buffer + * + * @return An IoT Error Type defining successful/failed call + */ +IoT_Error_t aws_iot_mqtt_internal_deserialize_publish(uint8_t *dup, QoS *qos, + uint8_t *retained, uint16_t *pPacketId, + char **pTopicName, uint16_t *topicNameLen, + unsigned char **payload, size_t *payloadLen, + unsigned char *pRxBuf, size_t rxBufLen) { + unsigned char *curData = pRxBuf; + unsigned char *endData = NULL; + IoT_Error_t rc = FAILURE; + uint32_t decodedLen = 0; + uint32_t readBytesLen = 0; + MQTTHeader header = {0}; + + FUNC_ENTRY; + + if(NULL == dup || NULL == qos || NULL == retained || NULL == pPacketId) { + FUNC_EXIT_RC(FAILURE); + } + + /* Publish header size is at least four bytes. + * Fixed header is two bytes. + * Variable header size depends on QoS And Topic Name. + * QoS level 0 doesn't have a message identifier (0 - 2 bytes) + * Topic Name length fields decide size of topic name field (at least 2 bytes) + * MQTT v3.1.1 Specification 3.3.1 */ + if(4 > rxBufLen) { + FUNC_EXIT_RC(MQTT_RX_BUFFER_TOO_SHORT_ERROR); + } + + header.byte = aws_iot_mqtt_internal_read_char(&curData); + if(PUBLISH != MQTT_HEADER_FIELD_TYPE(header.byte)) { + FUNC_EXIT_RC(FAILURE); + } + + *dup = MQTT_HEADER_FIELD_DUP(header.byte); + *qos = (QoS) MQTT_HEADER_FIELD_QOS(header.byte); + *retained = MQTT_HEADER_FIELD_RETAIN(header.byte); + + /* read remaining length */ + rc = aws_iot_mqtt_internal_decode_remaining_length_from_buffer(curData, &decodedLen, &readBytesLen); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + curData += (readBytesLen); + endData = curData + decodedLen; + + /* do we have enough data to read the protocol version byte? */ + if(AWS_SUCCESS != _aws_iot_mqtt_read_string_with_len(pTopicName, topicNameLen, &curData, endData) + || (0 > (endData - curData))) { + FUNC_EXIT_RC(FAILURE); + } + + if(QOS0 != *qos) { + *pPacketId = aws_iot_mqtt_internal_read_uint16_t(&curData); + } + + *payloadLen = (size_t) (endData - curData); + *payload = curData; + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * Deserializes the supplied (wire) buffer into an ack + * @param pPacketType returned integer - the MQTT packet type + * @param dup returned integer - the MQTT dup flag + * @param pPacketId returned integer - the MQTT packet identifier + * @param pRxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param rxBuflen the length in bytes of the data in the supplied buffer + * + * @return An IoT Error Type defining successful/failed call + */ +IoT_Error_t aws_iot_mqtt_internal_deserialize_ack(unsigned char *pPacketType, unsigned char *dup, + uint16_t *pPacketId, unsigned char *pRxBuf, + size_t rxBuflen) { + IoT_Error_t rc = FAILURE; + unsigned char *curdata = pRxBuf; + unsigned char *enddata = NULL; + uint32_t decodedLen = 0; + uint32_t readBytesLen = 0; + MQTTHeader header = {0}; + + FUNC_ENTRY; + + if(NULL == pPacketType || NULL == dup || NULL == pPacketId || NULL == pRxBuf) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* PUBACK fixed header size is two bytes, variable header is 2 bytes, MQTT v3.1.1 Specification 3.4.1 */ + if(4 > rxBuflen) { + FUNC_EXIT_RC(MQTT_RX_BUFFER_TOO_SHORT_ERROR); + } + + + header.byte = aws_iot_mqtt_internal_read_char(&curdata); + *dup = MQTT_HEADER_FIELD_DUP(header.byte); + *pPacketType = MQTT_HEADER_FIELD_TYPE(header.byte); + + /* read remaining length */ + rc = aws_iot_mqtt_internal_decode_remaining_length_from_buffer(curdata, &decodedLen, &readBytesLen); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + curdata += (readBytesLen); + enddata = curdata + decodedLen; + + if(enddata - curdata < 2) { + FUNC_EXIT_RC(FAILURE); + } + + *pPacketId = aws_iot_mqtt_internal_read_uint16_t(&curdata); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_mqtt_client_subscribe.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,449 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_subscribe.c + * @brief MQTT client subscribe API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_mqtt_client_common_internal.h" + +/** + * Serializes the supplied subscribe data into the supplied buffer, ready for sending + * @param pTxBuf the buffer into which the packet will be serialized + * @param txBufLen the length in bytes of the supplied buffer + * @param dup unsigned char - the MQTT dup flag + * @param packetId uint16_t - the MQTT packet identifier + * @param topicCount - number of members in the topicFilters and reqQos arrays + * @param pTopicNameList - array of topic filter names + * @param pTopicNameLenList - array of length of topic filter names + * @param pRequestedQoSs - array of requested QoS + * @param pSerializedLen - the length of the serialized data + * + * @return An IoT Error Type defining successful/failed operation + */ +static IoT_Error_t _aws_iot_mqtt_serialize_subscribe(unsigned char *pTxBuf, size_t txBufLen, + unsigned char dup, uint16_t packetId, uint32_t topicCount, + const char **pTopicNameList, uint16_t *pTopicNameLenList, + QoS *pRequestedQoSs, uint32_t *pSerializedLen) { + unsigned char *ptr; + uint32_t itr, rem_len; + IoT_Error_t rc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + if(NULL == pTxBuf || NULL == pSerializedLen) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + ptr = pTxBuf; + rem_len = 2; /* packetId */ + + for(itr = 0; itr < topicCount; ++itr) { + rem_len += (uint32_t) (pTopicNameLenList[itr] + 2 + 1); /* topic + length + req_qos */ + } + + if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(rem_len) > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + rc = aws_iot_mqtt_internal_init_header(&header, SUBSCRIBE, QOS1, dup, 0); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + /* write header */ + aws_iot_mqtt_internal_write_char(&ptr, header.byte); + + /* write remaining length */ + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, rem_len); + + aws_iot_mqtt_internal_write_uint_16(&ptr, packetId); + + for(itr = 0; itr < topicCount; ++itr) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pTopicNameList[itr], pTopicNameLenList[itr]); + aws_iot_mqtt_internal_write_char(&ptr, (unsigned char) pRequestedQoSs[itr]); + } + + *pSerializedLen = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * Deserializes the supplied (wire) buffer into suback data + * @param pPacketId returned integer - the MQTT packet identifier + * @param maxExpectedQoSCount - the maximum number of members allowed in the grantedQoSs array + * @param pGrantedQoSCount returned uint32_t - number of members in the grantedQoSs array + * @param pGrantedQoSs returned array of QoS type - the granted qualities of service + * @param pRxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param rxBufLen the length in bytes of the data in the supplied buffer + * + * @return An IoT Error Type defining successful/failed operation + */ +static IoT_Error_t _aws_iot_mqtt_deserialize_suback(uint16_t *pPacketId, uint32_t maxExpectedQoSCount, + uint32_t *pGrantedQoSCount, QoS *pGrantedQoSs, + unsigned char *pRxBuf, size_t rxBufLen) { + unsigned char *curData, *endData; + uint32_t decodedLen, readBytesLen; + IoT_Error_t decodeRc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + if(NULL == pPacketId || NULL == pGrantedQoSCount || NULL == pGrantedQoSs) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + curData = pRxBuf; + endData = NULL; + decodeRc = FAILURE; + decodedLen = 0; + readBytesLen = 0; + + /* SUBACK header size is 4 bytes for header and at least one byte for QoS payload + * Need at least a 5 bytes buffer. MQTT3.1.1 specification 3.9 + */ + if(5 > rxBufLen) { + FUNC_EXIT_RC(MQTT_RX_BUFFER_TOO_SHORT_ERROR); + } + + header.byte = aws_iot_mqtt_internal_read_char(&curData); + if(SUBACK != MQTT_HEADER_FIELD_TYPE(header.byte)) { + FUNC_EXIT_RC(FAILURE); + } + + /* read remaining length */ + decodeRc = aws_iot_mqtt_internal_decode_remaining_length_from_buffer(curData, &decodedLen, &readBytesLen); + if(AWS_SUCCESS != decodeRc) { + FUNC_EXIT_RC(decodeRc); + } + + curData += (readBytesLen); + endData = curData + decodedLen; + if(endData - curData < 2) { + FUNC_EXIT_RC(FAILURE); + } + + *pPacketId = aws_iot_mqtt_internal_read_uint16_t(&curData); + + *pGrantedQoSCount = 0; + while(curData < endData) { + if(*pGrantedQoSCount > maxExpectedQoSCount) { + FUNC_EXIT_RC(FAILURE); + } + pGrantedQoSs[(*pGrantedQoSCount)++] = (QoS) aws_iot_mqtt_internal_read_char(&curData); + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/* Returns MAX_MESSAGE_HANDLERS value if no free index is available */ +static uint32_t _aws_iot_mqtt_get_free_message_handler_index(AWS_IoT_Client *pClient) { + uint32_t itr; + + FUNC_ENTRY; + + for(itr = 0; itr < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; itr++) { + if(pClient->clientData.messageHandlers[itr].topicName == NULL) { + break; + } + } + + FUNC_EXIT_RC((int)itr); +} + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. This is the internal function which is called by the + * subscribe API to perform the operation. Not meant to be called directly as + * it doesn't do validations or client state changes + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * @warning pTopicName and pApplicationHandlerData need to be static in memory. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to. pTopicName needs to be static in memory since + * no malloc are performed by the SDK + * @param topicNameLen Length of the topic name + * @param pApplicationHandler_t Reference to the handler function for this subscription + * @param pApplicationHandlerData Point to data passed to the callback. + * pApplicationHandlerData also needs to be static in memory since no malloc are performed by the SDK + * + * @return An IoT Error Type defining successful/failed subscription + */ +static IoT_Error_t _aws_iot_mqtt_internal_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, + uint16_t topicNameLen, QoS qos, + pApplicationHandler_t pApplicationHandler, + void *pApplicationHandlerData) { + uint16_t txPacketId, rxPacketId; + uint32_t serializedLen, indexOfFreeMessageHandler, count; + IoT_Error_t rc; + awsTimer timer; + QoS grantedQoS[3] = {QOS0, QOS0, QOS0}; + + FUNC_ENTRY; + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + serializedLen = 0; + count = 0; + txPacketId = aws_iot_mqtt_get_next_packet_id(pClient); + rxPacketId = 0; + + rc = _aws_iot_mqtt_serialize_subscribe(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 0, + txPacketId, 1, &pTopicName, &topicNameLen, &qos, &serializedLen); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + indexOfFreeMessageHandler = _aws_iot_mqtt_get_free_message_handler_index(pClient); + if(AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS <= indexOfFreeMessageHandler) { + FUNC_EXIT_RC(MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR); + } + + /* send the subscribe packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, serializedLen, &timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* wait for suback */ + rc = aws_iot_mqtt_internal_wait_for_read(pClient, SUBACK, &timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Granted QoS can be 0, 1 or 2 */ + rc = _aws_iot_mqtt_deserialize_suback(&rxPacketId, 1, &count, grantedQoS, pClient->clientData.readBuf, + pClient->clientData.readBufSize); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* TODO : Figure out how to test this before activating this check */ + //if(txPacketId != rxPacketId) { + /* Different SUBACK received than expected. Return error + * This can cause issues if the request timeout value is too small */ + // return RX_MESSAGE_INVALID_ERROR; + //} + + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].topicName = + pTopicName; + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].topicNameLen = + topicNameLen; + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].pApplicationHandler = + pApplicationHandler; + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].pApplicationHandlerData = + pApplicationHandlerData; + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].qos = qos; + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. This is the outer function which does the validations and + * calls the internal subscribe above to perform the actual operation. + * It is also responsible for client state changes + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * @warning pTopicName and pApplicationHandlerData need to be static in memory. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to. pTopicName needs to be static in memory since + * no malloc are performed by the SDK + * @param topicNameLen Length of the topic name + * @param pApplicationHandler_t Reference to the handler function for this subscription + * @param pApplicationHandlerData Point to data passed to the callback. + * pApplicationHandlerData also needs to be static in memory since no malloc are performed by the SDK + * + * @return An IoT Error Type defining successful/failed subscription + */ +IoT_Error_t aws_iot_mqtt_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen, + QoS qos, pApplicationHandler_t pApplicationHandler, void *pApplicationHandlerData) { + ClientState clientState; + IoT_Error_t rc, subRc; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pTopicName || NULL == pApplicationHandler) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + if(CLIENT_STATE_CONNECTED_IDLE != clientState && CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN != clientState) { + FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + subRc = _aws_iot_mqtt_internal_subscribe(pClient, pTopicName, topicNameLen, qos, + pApplicationHandler, pApplicationHandlerData); + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS, clientState); + if(AWS_SUCCESS == subRc && AWS_SUCCESS != rc) { + subRc = rc; + } + + FUNC_EXIT_RC(subRc); +} + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. + * This is the internal function which is called by the resubscribe API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed subscription + */ +static IoT_Error_t _aws_iot_mqtt_internal_resubscribe(AWS_IoT_Client *pClient) { + uint16_t packetId; + uint32_t len, count, existingSubCount, itr; + IoT_Error_t rc; + awsTimer timer; + QoS grantedQoS[3] = {QOS0, QOS0, QOS0}; + + FUNC_ENTRY; + + packetId = 0; + len = 0; + count = 0; + existingSubCount = _aws_iot_mqtt_get_free_message_handler_index(pClient); + + for(itr = 0; itr < existingSubCount; itr++) { + if(pClient->clientData.messageHandlers[itr].topicName == NULL) { + continue; + } + + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + rc = _aws_iot_mqtt_serialize_subscribe(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 0, + aws_iot_mqtt_get_next_packet_id(pClient), 1, + &(pClient->clientData.messageHandlers[itr].topicName), + &(pClient->clientData.messageHandlers[itr].topicNameLen), + &(pClient->clientData.messageHandlers[itr].qos), &len); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* send the subscribe packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, len, &timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* wait for suback */ + rc = aws_iot_mqtt_internal_wait_for_read(pClient, SUBACK, &timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Granted QoS can be 0, 1 or 2 */ + rc = _aws_iot_mqtt_deserialize_suback(&packetId, 1, &count, grantedQoS, pClient->clientData.readBuf, + pClient->clientData.readBufSize); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. + * This is the outer function which does the validations and calls the internal resubscribe above + * to perform the actual operation. It is also responsible for client state changes + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed subscription + */ +IoT_Error_t aws_iot_mqtt_resubscribe(AWS_IoT_Client *pClient) { + IoT_Error_t rc, resubRc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(false == aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + if(CLIENT_STATE_CONNECTED_IDLE != aws_iot_mqtt_get_client_state(pClient)) { + FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, + CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + resubRc = _aws_iot_mqtt_internal_resubscribe(pClient); + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS, + CLIENT_STATE_CONNECTED_IDLE); + if(AWS_SUCCESS == resubRc && AWS_SUCCESS != rc) { + resubRc = rc; + } + + FUNC_EXIT_RC(resubRc); +} + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_mqtt_client_unsubscribe.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,250 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_unsubscribe.c + * @brief MQTT client unsubscribe API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_mqtt_client_common_internal.h" + +/** + * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending + * @param pTxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param txBufLen the length in bytes of the data in the supplied buffer + * @param dup integer - the MQTT dup flag + * @param packetId integer - the MQTT packet identifier + * @param count - number of members in the topicFilters array + * @param pTopicNameList - array of topic filter names + * @param pTopicNameLenList - array of length of topic filter names in pTopicNameList + * @param pSerializedLen - the length of the serialized data + * @return IoT_Error_t indicating function execution status + */ +static IoT_Error_t _aws_iot_mqtt_serialize_unsubscribe(unsigned char *pTxBuf, size_t txBufLen, + uint8_t dup, uint16_t packetId, + uint32_t count, const char **pTopicNameList, + uint16_t *pTopicNameLenList, uint32_t *pSerializedLen) { + unsigned char *ptr = pTxBuf; + uint32_t i = 0; + uint32_t rem_len = 2; /* packetId */ + IoT_Error_t rc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + + for(i = 0; i < count; ++i) { + rem_len += (uint32_t) (pTopicNameLenList[i] + 2); /* topic + length */ + } + + if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(rem_len) > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + rc = aws_iot_mqtt_internal_init_header(&header, UNSUBSCRIBE, QOS1, dup, 0); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + aws_iot_mqtt_internal_write_char(&ptr, header.byte); /* write header */ + + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, rem_len); /* write remaining length */ + + aws_iot_mqtt_internal_write_uint_16(&ptr, packetId); + + for(i = 0; i < count; ++i) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pTopicNameList[i], pTopicNameLenList[i]); + } + + *pSerializedLen = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + + +/** + * Deserializes the supplied (wire) buffer into unsuback data + * @param pPacketId returned integer - the MQTT packet identifier + * @param pRxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param rxBufLen the length in bytes of the data in the supplied buffer + * @return IoT_Error_t indicating function execution status + */ +static IoT_Error_t _aws_iot_mqtt_deserialize_unsuback(uint16_t *pPacketId, unsigned char *pRxBuf, size_t rxBufLen) { + unsigned char type = 0; + unsigned char dup = 0; + IoT_Error_t rc; + + FUNC_ENTRY; + + rc = aws_iot_mqtt_internal_deserialize_ack(&type, &dup, pPacketId, pRxBuf, rxBufLen); + if(AWS_SUCCESS == rc && UNSUBACK != type) { + rc = FAILURE; + } + + FUNC_EXIT_RC(rc); +} + +/** + * @brief Unsubscribe to an MQTT topic. + * + * Called to send an unsubscribe message to the broker requesting removal of a subscription + * to an MQTT topic. + * @note Call is blocking. The call returns after the receipt of the UNSUBACK control packet. + * This is the internal function which is called by the unsubscribe API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * + * @return An IoT Error Type defining successful/failed unsubscribe call + */ +static IoT_Error_t _aws_iot_mqtt_internal_unsubscribe(AWS_IoT_Client *pClient, const char *pTopicFilter, + uint16_t topicFilterLen) { + /* No NULL checks because this is a static internal function */ + + awsTimer timer; + + uint16_t packet_id; + uint32_t serializedLen = 0; + uint32_t i = 0; + IoT_Error_t rc; + bool subscriptionExists = false; + + FUNC_ENTRY; + + /* Remove from message handler array */ + for(i = 0; i < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; ++i) { + if(pClient->clientData.messageHandlers[i].topicName != NULL && + (strcmp(pClient->clientData.messageHandlers[i].topicName, pTopicFilter) == 0)) { + subscriptionExists = true; + break; + } + } + + if(false == subscriptionExists) { + FUNC_EXIT_RC(FAILURE); + } + + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + rc = _aws_iot_mqtt_serialize_unsubscribe(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 0, + aws_iot_mqtt_get_next_packet_id(pClient), 1, &pTopicFilter, + &topicFilterLen, &serializedLen); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* send the unsubscribe packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, serializedLen, &timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = aws_iot_mqtt_internal_wait_for_read(pClient, UNSUBACK, &timer); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = _aws_iot_mqtt_deserialize_unsuback(&packet_id, pClient->clientData.readBuf, pClient->clientData.readBufSize); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Remove from message handler array */ + for(i = 0; i < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; ++i) { + if(pClient->clientData.messageHandlers[i].topicName != NULL && + (strcmp(pClient->clientData.messageHandlers[i].topicName, pTopicFilter) == 0)) { + pClient->clientData.messageHandlers[i].topicName = NULL; + /* We don't want to break here, in case the same topic is registered + * with 2 callbacks. Unlikely scenario */ + } + } + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * @brief Unsubscribe to an MQTT topic. + * + * Called to send an unsubscribe message to the broker requesting removal of a subscription + * to an MQTT topic. + * @note Call is blocking. The call returns after the receipt of the UNSUBACK control packet. + * This is the outer function which does the validations and calls the internal unsubscribe above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * + * @return An IoT Error Type defining successful/failed unsubscribe call + */ +IoT_Error_t aws_iot_mqtt_unsubscribe(AWS_IoT_Client *pClient, const char *pTopicFilter, uint16_t topicFilterLen) { + IoT_Error_t rc, unsubRc; + ClientState clientState; + + if(NULL == pClient || NULL == pTopicFilter) { + return NULL_VALUE_ERROR; + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + return NETWORK_DISCONNECTED_ERROR; + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + if(CLIENT_STATE_CONNECTED_IDLE != clientState && CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN != clientState) { + return MQTT_CLIENT_NOT_IDLE_ERROR; + } + + rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS); + if(AWS_SUCCESS != rc) { + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS, clientState); + return rc; + } + + unsubRc = _aws_iot_mqtt_internal_unsubscribe(pClient, pTopicFilter, topicFilterLen); + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS, clientState); + if(AWS_SUCCESS == unsubRc && AWS_SUCCESS != rc) { + unsubRc = rc; + } + + return unsubRc; +} + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_mqtt_client_yield.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,322 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_yield.c + * @brief MQTT client yield API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_mqtt_client_common_internal.h" + +/** + * This is for the case when the aws_iot_mqtt_internal_send_packet Fails. + */ +static void _aws_iot_mqtt_force_client_disconnect(AWS_IoT_Client *pClient) { + pClient->clientStatus.clientState = CLIENT_STATE_DISCONNECTED_ERROR; + pClient->networkStack.disconnect(&(pClient->networkStack)); + pClient->networkStack.destroy(&(pClient->networkStack)); +} + +static IoT_Error_t _aws_iot_mqtt_handle_disconnect(AWS_IoT_Client *pClient) { + IoT_Error_t rc; + + FUNC_ENTRY; +//printf("JMF: called aws_iot_mqtt_handle_disconnect\n"); + rc = aws_iot_mqtt_disconnect(pClient); + if(rc != AWS_SUCCESS) { + // If the aws_iot_mqtt_internal_send_packet prevents us from sending a disconnect packet then we have to clean the stack + _aws_iot_mqtt_force_client_disconnect(pClient); + } + + if(NULL != pClient->clientData.disconnectHandler) { + pClient->clientData.disconnectHandler(pClient, pClient->clientData.disconnectHandlerData); + } + + /* Reset to 0 since this was not a manual disconnect */ + pClient->clientStatus.clientState = CLIENT_STATE_DISCONNECTED_ERROR; +//printf("JMF: %s:%d\n",__FILE__,__LINE__); + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); +} + + +static IoT_Error_t _aws_iot_mqtt_handle_reconnect(AWS_IoT_Client *pClient) { + IoT_Error_t rc; + + FUNC_ENTRY; + + if(!has_timer_expired(&(pClient->reconnectDelayTimer))) { + /* awsTimer has not expired. Not time to attempt reconnect yet. + * Return attempting reconnect */ + FUNC_EXIT_RC(NETWORK_ATTEMPTING_RECONNECT); + } + + rc = NETWORK_PHYSICAL_LAYER_DISCONNECTED; + if(NULL != pClient->networkStack.isConnected) { + rc = pClient->networkStack.isConnected(&(pClient->networkStack)); + } + + if(NETWORK_PHYSICAL_LAYER_CONNECTED == rc) { + rc = aws_iot_mqtt_attempt_reconnect(pClient); + if(NETWORK_RECONNECTED == rc) { + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, + CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + FUNC_EXIT_RC(NETWORK_RECONNECTED); + } + } + + pClient->clientData.currentReconnectWaitInterval *= 2; + + if(AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL < pClient->clientData.currentReconnectWaitInterval) { + FUNC_EXIT_RC(NETWORK_RECONNECT_TIMED_OUT_ERROR); + } + countdown_ms(&(pClient->reconnectDelayTimer), pClient->clientData.currentReconnectWaitInterval); + FUNC_EXIT_RC(rc); +} + +static IoT_Error_t _aws_iot_mqtt_keep_alive(AWS_IoT_Client *pClient) { + IoT_Error_t rc = AWS_SUCCESS; + awsTimer timer; + size_t serialized_len; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(0 == pClient->clientData.keepAliveInterval) { + FUNC_EXIT_RC(AWS_SUCCESS); + } + + if(!has_timer_expired(&pClient->pingTimer)) { + FUNC_EXIT_RC(AWS_SUCCESS); + } + + if(pClient->clientStatus.isPingOutstanding) { +//printf("JMF1\n"); + rc = _aws_iot_mqtt_handle_disconnect(pClient); + FUNC_EXIT_RC(rc); + } + + /* there is no ping outstanding - send one */ + init_timer(&timer); + + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + serialized_len = 0; + rc = aws_iot_mqtt_internal_serialize_zero(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, + PINGREQ, &serialized_len); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* send the ping packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, serialized_len, &timer); + if(AWS_SUCCESS != rc) { + //If sending a PING fails we can no longer determine if we are connected. In this case we decide we are disconnected and begin reconnection attempts +//printf("JMF2\n"); + rc = _aws_iot_mqtt_handle_disconnect(pClient); + FUNC_EXIT_RC(rc); + } + + pClient->clientStatus.isPingOutstanding = true; + /* start a timer to wait for PINGRESP from server */ + countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +/** + * @brief Yield to the MQTT client + * + * Called to yield the current thread to the underlying MQTT client. This time is used by + * the MQTT client to manage PING requests to monitor the health of the TCP connection as + * well as periodically check the socket receive buffer for subscribe messages. Yield() + * must be called at a rate faster than the keepalive interval. It must also be called + * at a rate faster than the incoming message rate as this is the only way the client receives + * processing time to manage incoming messages. + * This is the internal function which is called by the yield API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. + * + * @return An IoT Error Type defining successful/failed client processing. + * If this call results in an error it is likely the MQTT connection has dropped. + * iot_is_mqtt_connected can be called to confirm. + */ +static IoT_Error_t _aws_iot_mqtt_internal_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms) { + IoT_Error_t yieldRc = AWS_SUCCESS; + + uint8_t packet_type; + ClientState clientState; + awsTimer timer; + init_timer(&timer); + countdown_ms(&timer, timeout_ms); +//printf("JMF: called internal_yeld\n"); + FUNC_ENTRY; + + // evaluate timeout at the end of the loop to make sure the actual yield runs at least once + do { + clientState = aws_iot_mqtt_get_client_state(pClient); + if(CLIENT_STATE_PENDING_RECONNECT == clientState) { +//printf("JMF: pending_reconnect\n"); + if(AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL < pClient->clientData.currentReconnectWaitInterval) { + yieldRc = NETWORK_RECONNECT_TIMED_OUT_ERROR; + break; + } +//printf("JMF: do reconnect\n"); + yieldRc = _aws_iot_mqtt_handle_reconnect(pClient); + /* Network reconnect attempted, check if yield timer expired before + * doing anything else */ + continue; + } + +//printf("JMF: do internal_cycle_read \n"); + yieldRc = aws_iot_mqtt_internal_cycle_read(pClient, &timer, &packet_type); + if(AWS_SUCCESS == yieldRc) { + yieldRc = _aws_iot_mqtt_keep_alive(pClient); + } else { + // SSL read and write errors are terminal, connection must be closed and retried + if(NETWORK_SSL_READ_ERROR == yieldRc || NETWORK_SSL_READ_TIMEOUT_ERROR == yieldRc + || NETWORK_SSL_WRITE_ERROR == yieldRc || NETWORK_SSL_WRITE_TIMEOUT_ERROR == yieldRc) { +//printf("JMF3 %d\n",yieldRc); + yieldRc = _aws_iot_mqtt_handle_disconnect(pClient); + } + } + +//printf("JMF: keepalive said: %d \n", yieldRc); + if(NETWORK_DISCONNECTED_ERROR == yieldRc) { +//printf("JMF: was a disconnect erro \n"); + pClient->clientData.counterNetworkDisconnected++; + if(1 == pClient->clientStatus.isAutoReconnectEnabled) { + yieldRc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_DISCONNECTED_ERROR, + CLIENT_STATE_PENDING_RECONNECT); + if(AWS_SUCCESS != yieldRc) { + FUNC_EXIT_RC(yieldRc); + } + + pClient->clientData.currentReconnectWaitInterval = AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL; + countdown_ms(&(pClient->reconnectDelayTimer), pClient->clientData.currentReconnectWaitInterval); + /* Depending on timer values, it is possible that yield timer has expired + * Set to rc to attempting reconnect to inform client that autoreconnect + * attempt has started */ + yieldRc = NETWORK_ATTEMPTING_RECONNECT; + } else { + break; + } + } else if(AWS_SUCCESS != yieldRc) { + break; + } + } while(!has_timer_expired(&timer)); +//printf("JMF: exit internal_yield with %d\n",yieldRc); + + FUNC_EXIT_RC(yieldRc); +} + +/** + * @brief Yield to the MQTT client + * + * Called to yield the current thread to the underlying MQTT client. This time is used by + * the MQTT client to manage PING requests to monitor the health of the TCP connection as + * well as periodically check the socket receive buffer for subscribe messages. Yield() + * must be called at a rate faster than the keepalive interval. It must also be called + * at a rate faster than the incoming message rate as this is the only way the client receives + * processing time to manage incoming messages. + * This is the outer function which does the validations and calls the internal yield above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. + * + * @return An IoT Error Type defining successful/failed client processing. + * If this call results in an error it is likely the MQTT connection has dropped. + * iot_is_mqtt_connected can be called to confirm. + */ +IoT_Error_t aws_iot_mqtt_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms) { + IoT_Error_t rc, yieldRc; + ClientState clientState; + + if(NULL == pClient || 0 == timeout_ms) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + /* Check if network was manually disconnected */ + if(CLIENT_STATE_DISCONNECTED_MANUALLY == clientState) { + FUNC_EXIT_RC(NETWORK_MANUALLY_DISCONNECTED); + } + + /* If we are in the pending reconnect state, skip other checks. + * Pending reconnect state is only set when auto-reconnect is enabled */ + if(CLIENT_STATE_PENDING_RECONNECT != clientState) { + /* Check if network is disconnected and auto-reconnect is not enabled */ + if(!aws_iot_mqtt_is_client_connected(pClient)) { +//printf("JMF: %s:%d\n",__FILE__,__LINE__); + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + /* Check if client is idle, if not another operation is in progress and we should return */ + if(CLIENT_STATE_CONNECTED_IDLE != clientState) { + FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, + CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + } + + yieldRc = _aws_iot_mqtt_internal_yield(pClient, timeout_ms); + + if(NETWORK_DISCONNECTED_ERROR != yieldRc && NETWORK_ATTEMPTING_RECONNECT != yieldRc) { + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS, + CLIENT_STATE_CONNECTED_IDLE); + if(AWS_SUCCESS == yieldRc && AWS_SUCCESS != rc) { + yieldRc = rc; + } + } + + FUNC_EXIT_RC(yieldRc); +} + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_shadow.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,250 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_shadow.c + * @brief Shadow client API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <string.h> +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_shadow_interface.h" +#include "aws_iot_error.h" +#include "aws_iot_log.h" +#include "aws_iot_shadow_actions.h" +#include "aws_iot_shadow_json.h" +#include "aws_iot_shadow_key.h" +#include "aws_iot_shadow_records.h" + +const ShadowInitParameters_t ShadowInitParametersDefault = {(char *) AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT, NULL, NULL, + NULL, false, NULL}; + +const ShadowConnectParameters_t ShadowConnectParametersDefault = {(char *) AWS_IOT_MY_THING_NAME, + (char *) AWS_IOT_MQTT_CLIENT_ID, 0, NULL}; + +static char deleteAcceptedTopic[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + +void aws_iot_shadow_reset_last_received_version(void) { + shadowJsonVersionNum = 0; +} + +uint32_t aws_iot_shadow_get_last_received_version(void) { + return shadowJsonVersionNum; +} + +void aws_iot_shadow_enable_discard_old_delta_msgs(void) { + shadowDiscardOldDeltaFlag = true; +} + +void aws_iot_shadow_disable_discard_old_delta_msgs(void) { + shadowDiscardOldDeltaFlag = false; +} + +IoT_Error_t aws_iot_shadow_free(AWS_IoT_Client *pClient) +{ + IoT_Error_t rc; + + if (NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + rc = aws_iot_mqtt_free(pClient); + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_init(AWS_IoT_Client *pClient, ShadowInitParameters_t *pParams) { + IoT_Client_Init_Params mqttInitParams = IoT_Client_Init_Params_initializer; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pParams) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + mqttInitParams.enableAutoReconnect = pParams->enableAutoReconnect; + mqttInitParams.pHostURL = pParams->pHost; + mqttInitParams.port = pParams->port; + mqttInitParams.pRootCALocation = pParams->pRootCA; + mqttInitParams.pDeviceCertLocation = pParams->pClientCRT; + mqttInitParams.pDevicePrivateKeyLocation = pParams->pClientKey; + mqttInitParams.mqttPacketTimeout_ms = 5000; + mqttInitParams.mqttCommandTimeout_ms = 20000; + mqttInitParams.tlsHandshakeTimeout_ms = 5000; + mqttInitParams.isSSLHostnameVerify = true; + mqttInitParams.disconnectHandler = pParams->disconnectHandler; + + rc = aws_iot_mqtt_init(pClient, &mqttInitParams); + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + resetClientTokenSequenceNum(); + aws_iot_shadow_reset_last_received_version(); + initDeltaTokens(); + + FUNC_EXIT_RC(AWS_SUCCESS); +} + +IoT_Error_t aws_iot_shadow_connect(AWS_IoT_Client *pClient, ShadowConnectParameters_t *pParams) { + IoT_Error_t rc = AWS_SUCCESS; + uint16_t deleteAcceptedTopicLen; + IoT_Client_Connect_Params ConnectParams = iotClientConnectParamsDefault; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pParams || NULL == pParams->pMqttClientId) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + snprintf(myThingName, MAX_SIZE_OF_THING_NAME, "%s", pParams->pMyThingName); + snprintf(mqttClientID, MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES, "%s", pParams->pMqttClientId); + + ConnectParams.keepAliveIntervalInSec = 600; // NOTE: Temporary fix + ConnectParams.MQTTVersion = MQTT_3_1_1; + ConnectParams.isCleanSession = true; + ConnectParams.isWillMsgPresent = false; + ConnectParams.pClientID = pParams->pMqttClientId; + ConnectParams.clientIDLen = pParams->mqttClientIdLen; + ConnectParams.pPassword = NULL; + ConnectParams.pUsername = NULL; + + rc = aws_iot_mqtt_connect(pClient, &ConnectParams); + + if(AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + initializeRecords(pClient); + + if(NULL != pParams->deleteActionHandler) { + snprintf(deleteAcceptedTopic, MAX_SHADOW_TOPIC_LENGTH_BYTES, + "$aws/things/%s/shadow/delete/accepted", myThingName); + deleteAcceptedTopicLen = (uint16_t) strlen(deleteAcceptedTopic); + rc = aws_iot_mqtt_subscribe(pClient, deleteAcceptedTopic, deleteAcceptedTopicLen, QOS1, + pParams->deleteActionHandler, (void *) myThingName); + } + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_register_delta(AWS_IoT_Client *pMqttClient, jsonStruct_t *pStruct) { + if(NULL == pMqttClient || NULL == pStruct) { + return NULL_VALUE_ERROR; + } + + if(!aws_iot_mqtt_is_client_connected(pMqttClient)) { + return MQTT_CONNECTION_ERROR; + } + + return registerJsonTokenOnDelta(pStruct); +} + +IoT_Error_t aws_iot_shadow_yield(AWS_IoT_Client *pClient, uint32_t timeout) { + if(NULL == pClient) { + return NULL_VALUE_ERROR; + } + + HandleExpiredResponseCallbacks(); + return aws_iot_mqtt_yield(pClient, timeout); +} + +IoT_Error_t aws_iot_shadow_disconnect(AWS_IoT_Client *pClient) { + return aws_iot_mqtt_disconnect(pClient); +} + +IoT_Error_t aws_iot_shadow_update(AWS_IoT_Client *pClient, const char *pThingName, char *pJsonString, + fpActionCallback_t callback, void *pContextData, uint8_t timeout_seconds, + bool isPersistentSubscribe) { + IoT_Error_t rc; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(MQTT_CONNECTION_ERROR); + } + + rc = aws_iot_shadow_internal_action(pThingName, SHADOW_UPDATE, pJsonString, strlen(pJsonString), callback, pContextData, + timeout_seconds, isPersistentSubscribe); + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_delete(AWS_IoT_Client *pClient, const char *pThingName, fpActionCallback_t callback, + void *pContextData, uint8_t timeout_seconds, bool isPersistentSubscribe) { + char deleteRequestJsonBuf[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE]; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(MQTT_CONNECTION_ERROR); + } + + rc = aws_iot_shadow_internal_delete_request_json(deleteRequestJsonBuf, MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE ); + if ( AWS_SUCCESS != rc ) { + FUNC_EXIT_RC( rc ); + } + + rc = aws_iot_shadow_internal_action(pThingName, SHADOW_DELETE, deleteRequestJsonBuf, MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE, callback, pContextData, + timeout_seconds, isPersistentSubscribe); + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_get(AWS_IoT_Client *pClient, const char *pThingName, fpActionCallback_t callback, + void *pContextData, uint8_t timeout_seconds, bool isPersistentSubscribe) { + char getRequestJsonBuf[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE]; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(MQTT_CONNECTION_ERROR); + } + + rc = aws_iot_shadow_internal_get_request_json(getRequestJsonBuf, MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE ); + if (AWS_SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = aws_iot_shadow_internal_action(pThingName, SHADOW_GET, getRequestJsonBuf, MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE, callback, pContextData, + timeout_seconds, isPersistentSubscribe); + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_set_autoreconnect_status(AWS_IoT_Client *pClient, bool newStatus) { + return aws_iot_mqtt_autoreconnect_set_status(pClient, newStatus); +} + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_shadow_actions.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,80 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_shadow_actions.c + * @brief Shadow client Action API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_shadow_actions.h" + +#include "aws_iot_log.h" +#include "aws_iot_shadow_json.h" +#include "aws_iot_shadow_records.h" +#include "aws_iot_config.h" + +IoT_Error_t aws_iot_shadow_internal_action(const char *pThingName, ShadowActions_t action, + const char *pJsonDocumentToBeSent, size_t jsonSize, fpActionCallback_t callback, + void *pCallbackContext, uint32_t timeout_seconds, bool isSticky) { + IoT_Error_t ret_val = AWS_SUCCESS; + bool isClientTokenPresent = false; + bool isAckWaitListFree = false; + uint8_t indexAckWaitList; + char extractedClientToken[MAX_SIZE_CLIENT_ID_WITH_SEQUENCE]; + + FUNC_ENTRY; + + if(NULL == pThingName || NULL == pJsonDocumentToBeSent) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + isClientTokenPresent = extractClientToken(pJsonDocumentToBeSent, jsonSize, extractedClientToken, MAX_SIZE_CLIENT_ID_WITH_SEQUENCE ); + + if(isClientTokenPresent && (NULL != callback)) { + if(getNextFreeIndexOfAckWaitList(&indexAckWaitList)) { + isAckWaitListFree = true; + } + + if(isAckWaitListFree) { + if(!isSubscriptionPresent(pThingName, action)) { + ret_val = subscribeToShadowActionAcks(pThingName, action, isSticky); + } else { + incrementSubscriptionCnt(pThingName, action, isSticky); + } + } + else { + ret_val = FAILURE; + } + } + + if(AWS_SUCCESS == ret_val) { + ret_val = publishToShadowAction(pThingName, action, pJsonDocumentToBeSent); + } + + if(isClientTokenPresent && (NULL != callback) && (AWS_SUCCESS == ret_val) && isAckWaitListFree) { + addToAckWaitList(indexAckWaitList, pThingName, action, extractedClientToken, callback, pCallbackContext, + timeout_seconds); + } + + FUNC_EXIT_RC(ret_val); +} + +#ifdef __cplusplus +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_shadow_json.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,534 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_shadow_json.c + * @brief Shadow client JSON parsing API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_shadow_json.h" + +#include <string.h> +#include <stdbool.h> + +#include "aws_iot_json_utils.h" +#include "aws_iot_log.h" +#include "aws_iot_shadow_key.h" +#include "aws_iot_config.h" + +extern char mqttClientID[MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES]; +#define AWS_IOT_SHADOW_CLIENT_TOKEN_KEY "{\"clientToken\":\"" +static uint32_t clientTokenNum = 0; + +//helper functions +static IoT_Error_t convertDataToString(char *pStringBuffer, size_t maxSizoStringBuffer, JsonPrimitiveType type, + void *pData); + +void resetClientTokenSequenceNum(void) { + clientTokenNum = 0; +} + +static IoT_Error_t emptyJsonWithClientToken(char *pBuffer, size_t bufferSize) { + + IoT_Error_t rc = AWS_SUCCESS; + size_t dataLenInBuffer = 0; + + if(pBuffer != NULL) + { + dataLenInBuffer = (size_t)snprintf(pBuffer, bufferSize, AWS_IOT_SHADOW_CLIENT_TOKEN_KEY); + }else + { + IOT_ERROR("NULL buffer in emptyJsonWithClientToken\n"); + rc = FAILURE; + } + + if(rc == AWS_SUCCESS) + { + if ( dataLenInBuffer < bufferSize ) + { + dataLenInBuffer += (size_t)snprintf(pBuffer + dataLenInBuffer, bufferSize - dataLenInBuffer, "%s-%d", mqttClientID, ( int )clientTokenNum++); + } + else + { + rc = FAILURE; + IOT_ERROR("Supplied buffer too small to create JSON file\n"); + } + } + + if(rc == AWS_SUCCESS) + { + if ( dataLenInBuffer < bufferSize ) + { + dataLenInBuffer += (size_t)snprintf( pBuffer + dataLenInBuffer, bufferSize - dataLenInBuffer, "\"}" ); + if ( dataLenInBuffer > bufferSize ) + { + rc = FAILURE; + IOT_ERROR( "Supplied buffer too small to create JSON file\n" ); + } + } + else + { + rc = FAILURE; + IOT_ERROR( "Supplied buffer too small to create JSON file\n" ); + } + } + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_internal_get_request_json(char *pBuffer, size_t bufferSize) { + return emptyJsonWithClientToken( pBuffer, bufferSize); +} + +IoT_Error_t aws_iot_shadow_internal_delete_request_json(char *pBuffer, size_t bufferSize ) { + return emptyJsonWithClientToken( pBuffer, bufferSize); +} + +static inline IoT_Error_t checkReturnValueOfSnPrintf(int32_t snPrintfReturn, size_t maxSizeOfJsonDocument) { + if(snPrintfReturn < 0) { + return SHADOW_JSON_ERROR; + } else if((size_t) snPrintfReturn >= maxSizeOfJsonDocument) { + return SHADOW_JSON_BUFFER_TRUNCATED; + } + return AWS_SUCCESS; +} + +IoT_Error_t aws_iot_shadow_init_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument) { + + IoT_Error_t ret_val = AWS_SUCCESS; + int32_t snPrintfReturn = 0; + + if(pJsonDocument == NULL) { + return NULL_VALUE_ERROR; + } + snPrintfReturn = snprintf(pJsonDocument, maxSizeOfJsonDocument, "{\"state\":{"); + + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, maxSizeOfJsonDocument); + + return ret_val; + +} + +IoT_Error_t aws_iot_shadow_add_desired(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...) { + IoT_Error_t ret_val = AWS_SUCCESS; + size_t tempSize = 0; + int8_t i; + size_t remSizeOfJsonBuffer = maxSizeOfJsonDocument; + int32_t snPrintfReturn = 0; + va_list pArgs; + jsonStruct_t *pTemporary = NULL; + va_start(pArgs, count); + + if(pJsonDocument == NULL) { + va_end(pArgs); + return NULL_VALUE_ERROR; + } + + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + va_end(pArgs); + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"desired\":{"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + if(ret_val != AWS_SUCCESS) { + va_end(pArgs); + return ret_val; + } + + for(i = 0; i < count; i++) { + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + va_end(pArgs); + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + pTemporary = va_arg (pArgs, jsonStruct_t *); + if(pTemporary != NULL) { + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"%s\":", + pTemporary->pKey); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + if(ret_val != AWS_SUCCESS) { + va_end(pArgs); + return ret_val; + } + if(pTemporary->pKey != NULL && pTemporary->pData != NULL) { + ret_val = convertDataToString(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, + pTemporary->type, pTemporary->pData); + } else { + va_end(pArgs); + return NULL_VALUE_ERROR; + } + if(ret_val != AWS_SUCCESS) { + va_end(pArgs); + return ret_val; + } + } else { + va_end(pArgs); + return NULL_VALUE_ERROR; + } + } + + va_end(pArgs); + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument) - 1, remSizeOfJsonBuffer, "},"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + return ret_val; +} + +IoT_Error_t aws_iot_shadow_add_reported(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...) { + IoT_Error_t ret_val = AWS_SUCCESS; + + int8_t i; + size_t remSizeOfJsonBuffer = maxSizeOfJsonDocument; + int32_t snPrintfReturn = 0; + size_t tempSize = 0; + jsonStruct_t *pTemporary; + va_list pArgs; + va_start(pArgs, count); + + if(pJsonDocument == NULL) { + va_end(pArgs); + return NULL_VALUE_ERROR; + } + + + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + va_end(pArgs); + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"reported\":{"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + if(ret_val != AWS_SUCCESS) { + va_end(pArgs); + return ret_val; + } + + for(i = 0; i < count; i++) { + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + va_end(pArgs); + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + pTemporary = va_arg (pArgs, jsonStruct_t *); + if(pTemporary != NULL) { + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"%s\":", + pTemporary->pKey); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + if(ret_val != AWS_SUCCESS) { + va_end(pArgs); + return ret_val; + } + if(pTemporary->pKey != NULL && pTemporary->pData != NULL) { + ret_val = convertDataToString(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, + pTemporary->type, pTemporary->pData); + } else { + va_end(pArgs); + return NULL_VALUE_ERROR; + } + if(ret_val != AWS_SUCCESS) { + va_end(pArgs); + return ret_val; + } + } else { + va_end(pArgs); + return NULL_VALUE_ERROR; + } + } + + va_end(pArgs); + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument) - 1, remSizeOfJsonBuffer, "},"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + return ret_val; +} + + +int32_t FillWithClientTokenSize(char *pBufferToBeUpdatedWithClientToken, size_t maxSizeOfJsonDocument) { + int32_t snPrintfReturn; + snPrintfReturn = snprintf(pBufferToBeUpdatedWithClientToken, maxSizeOfJsonDocument, "%s-%d", mqttClientID, + (int) clientTokenNum++); + + return snPrintfReturn; +} + +IoT_Error_t aws_iot_fill_with_client_token(char *pBufferToBeUpdatedWithClientToken, size_t maxSizeOfJsonDocument) { + + int32_t snPrintfRet = 0; + snPrintfRet = FillWithClientTokenSize(pBufferToBeUpdatedWithClientToken, maxSizeOfJsonDocument); + return checkReturnValueOfSnPrintf(snPrintfRet, maxSizeOfJsonDocument); + +} + +IoT_Error_t aws_iot_finalize_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument) { + size_t remSizeOfJsonBuffer = maxSizeOfJsonDocument; + int32_t snPrintfReturn = 0; + size_t tempSize = 0; + IoT_Error_t ret_val = AWS_SUCCESS; + + if(pJsonDocument == NULL) { + return NULL_VALUE_ERROR; + } + + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + // strlen(ShadowTxBuffer) - 1 is to ensure we remove the last ,(comma) that was added + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument) - 1, remSizeOfJsonBuffer, "}, \"%s\":\"", + SHADOW_CLIENT_TOKEN_STRING); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + if(ret_val != AWS_SUCCESS) { + return ret_val; + } + // refactor this XXX repeated code + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + + snPrintfReturn = FillWithClientTokenSize(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + if(ret_val != AWS_SUCCESS) { + return ret_val; + } + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"}"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + return ret_val; +} + +static IoT_Error_t convertDataToString(char *pStringBuffer, size_t maxSizoStringBuffer, JsonPrimitiveType type, + void *pData) { + int32_t snPrintfReturn = 0; + IoT_Error_t ret_val = AWS_SUCCESS; + + if(maxSizoStringBuffer == 0) { + return SHADOW_JSON_ERROR; + } + + if(type == SHADOW_JSON_INT32) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%li,", *(int32_t *) (pData)); + } else if(type == SHADOW_JSON_INT16) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%hi,", *(int16_t *) (pData)); + } else if(type == SHADOW_JSON_INT8) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%hhi,", *(int8_t *) (pData)); + } else if(type == SHADOW_JSON_UINT32) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%lu,", *(uint32_t *) (pData)); + } else if(type == SHADOW_JSON_UINT16) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%hu,", *(uint16_t *) (pData)); + } else if(type == SHADOW_JSON_UINT8) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%hhu,", *(uint8_t *) (pData)); + } else if(type == SHADOW_JSON_DOUBLE) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%f,", *(double *) (pData)); + } else if(type == SHADOW_JSON_FLOAT) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%f,", *(float *) (pData)); + } else if(type == SHADOW_JSON_BOOL) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%s,", *(bool *) (pData) ? "true" : "false"); + } else if(type == SHADOW_JSON_STRING) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "\"%s\",", (char *) (pData)); + } else if(type == SHADOW_JSON_OBJECT) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%s,", (char *) (pData)); + } + + + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, maxSizoStringBuffer); + + return ret_val; +} + +static jsmn_parser shadowJsonParser; +static jsmntok_t jsonTokenStruct[MAX_JSON_TOKEN_EXPECTED]; + +bool isJsonValidAndParse(const char *pJsonDocument, size_t jsonSize, void *pJsonHandler, int32_t *pTokenCount) { + int32_t tokenCount; + + IOT_UNUSED(pJsonHandler); + + jsmn_init(&shadowJsonParser); + + tokenCount = jsmn_parse(&shadowJsonParser, pJsonDocument, jsonSize, jsonTokenStruct, + sizeof(jsonTokenStruct) / sizeof(jsonTokenStruct[0])); + + if(tokenCount < 0) { + IOT_WARN("Failed to parse JSON: %ld\n", tokenCount); + return false; + } + + /* Assume the top-level element is an object */ + if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) { + IOT_WARN("Top Level is not an object\n"); + return false; + } + + *pTokenCount = tokenCount; + + return true; +} + +static IoT_Error_t UpdateValueIfNoObject(const char *pJsonString, jsonStruct_t *pDataStruct, jsmntok_t token) { + IoT_Error_t ret_val = SHADOW_JSON_ERROR; + if(pDataStruct->type == SHADOW_JSON_BOOL && pDataStruct->dataLength >= sizeof(bool)) { + ret_val = parseBooleanValue((bool *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_INT32 && pDataStruct->dataLength >= sizeof(int32_t)) { + ret_val = parseInteger32Value((int32_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_INT16 && pDataStruct->dataLength >= sizeof(int16_t)) { + ret_val = parseInteger16Value((int16_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_INT8 && pDataStruct->dataLength >= sizeof(int8_t)) { + ret_val = parseInteger8Value((int8_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_UINT32 && pDataStruct->dataLength >= sizeof(uint32_t)) { + ret_val = parseUnsignedInteger32Value((uint32_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_UINT16 && pDataStruct->dataLength >= sizeof(uint16_t)) { + ret_val = parseUnsignedInteger16Value((uint16_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_UINT8 && pDataStruct->dataLength >= sizeof(uint8_t)) { + ret_val = parseUnsignedInteger8Value((uint8_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_FLOAT && pDataStruct->dataLength >= sizeof(float)) { + ret_val = parseFloatValue((float *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_DOUBLE && pDataStruct->dataLength >= sizeof(double)) { + ret_val = parseDoubleValue((double *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_STRING) { + ret_val = parseStringValue((char *) pDataStruct->pData, pDataStruct->dataLength, pJsonString, &token); + } + + return ret_val; +} + +bool isJsonKeyMatchingAndUpdateValue(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount, + jsonStruct_t *pDataStruct, uint32_t *pDataLength, int32_t *pDataPosition) { + int32_t i; + uint32_t dataLength; + jsmntok_t dataToken; + + IOT_UNUSED(pJsonHandler); + + for(i = 1; i < tokenCount; i++) { + if(jsoneq(pJsonDocument, &(jsonTokenStruct[i]), pDataStruct->pKey) == 0) { + dataToken = jsonTokenStruct[i + 1]; + dataLength = (uint32_t) (dataToken.end - dataToken.start); + UpdateValueIfNoObject(pJsonDocument, pDataStruct, dataToken); + *pDataPosition = dataToken.start; + *pDataLength = dataLength; + return true; + } else if(jsoneq(pJsonDocument, &(jsonTokenStruct[i]), "metadata") == 0) { + return false; + } + } + return false; +} + +bool isReceivedJsonValid(const char *pJsonDocument, size_t jsonSize ) { + int32_t tokenCount; + + jsmn_init(&shadowJsonParser); + + tokenCount = jsmn_parse(&shadowJsonParser, pJsonDocument, jsonSize, jsonTokenStruct, + sizeof(jsonTokenStruct) / sizeof(jsonTokenStruct[0])); + + if(tokenCount < 0) { + IOT_WARN("Failed to parse JSON: %ld\n", tokenCount); + return false; + } + + /* Assume the top-level element is an object */ + if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) { + return false; + } + + return true; +} + +bool extractClientToken(const char *pJsonDocument, size_t jsonSize, char *pExtractedClientToken, size_t clientTokenSize) { + int32_t tokenCount, i; + size_t length; + jsmntok_t ClientJsonToken; + jsmn_init(&shadowJsonParser); + + tokenCount = jsmn_parse(&shadowJsonParser, pJsonDocument, jsonSize, jsonTokenStruct, + sizeof(jsonTokenStruct) / sizeof(jsonTokenStruct[0])); + + if(tokenCount < 0) { + IOT_WARN("Failed to parse JSON: %ld\n", tokenCount); + return false; + } + + /* Assume the top-level element is an object */ + if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) { + return false; + } + + for(i = 1; i < tokenCount; i++) { + if(jsoneq(pJsonDocument, &jsonTokenStruct[i], SHADOW_CLIENT_TOKEN_STRING) == 0) { + ClientJsonToken = jsonTokenStruct[i + 1]; + length = (uint8_t) (ClientJsonToken.end - ClientJsonToken.start); + if (clientTokenSize >= length + 1) + { + strncpy( pExtractedClientToken, pJsonDocument + ClientJsonToken.start, length); + pExtractedClientToken[length] = '\0'; + return true; + }else{ + IOT_WARN( "Token size %zu too small for string %zu \n", clientTokenSize, length); + return false; + } + } + } + + return false; +} + +bool extractVersionNumber(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount, uint32_t *pVersionNumber) { + int32_t i; + IoT_Error_t ret_val = AWS_SUCCESS; + + IOT_UNUSED(pJsonHandler); + + for(i = 1; i < tokenCount; i++) { + if(jsoneq(pJsonDocument, &(jsonTokenStruct[i]), SHADOW_VERSION_STRING) == 0) { + ret_val = parseUnsignedInteger32Value(pVersionNumber, pJsonDocument, &jsonTokenStruct[i + 1]); + if(ret_val == AWS_SUCCESS) { + return true; + } + } + } + return false; +} + +#ifdef __cplusplus +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/aws_iot_shadow_records.c Wed May 30 20:59:51 2018 +0000 @@ -0,0 +1,529 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/** + * @file aws_iot_mqtt_client_subscribe.c + * @brief MQTT client subscribe API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_shadow_records.h" + +#include <string.h> +#include <stdio.h> + +#include "timer_interface.h" +#include "aws_iot_json_utils.h" +#include "aws_iot_log.h" +#include "aws_iot_shadow_json.h" +#include "aws_iot_config.h" + +typedef struct { + char clientTokenID[MAX_SIZE_CLIENT_ID_WITH_SEQUENCE]; + char thingName[MAX_SIZE_OF_THING_NAME]; + ShadowActions_t action; + fpActionCallback_t callback; + void *pCallbackContext; + bool isFree; + awsTimer timer; +} ToBeReceivedAckRecord_t; + +typedef struct { + const char *pKey; + void *pStruct; + jsonStructCallback_t callback; + bool isFree; +} JsonTokenTable_t; + +typedef struct { + char Topic[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + uint8_t count; + bool isFree; + bool isSticky; +} SubscriptionRecord_t; + +typedef enum { + SHADOW_ACCEPTED, SHADOW_REJECTED, SHADOW_ACTION +} ShadowAckTopicTypes_t; + +ToBeReceivedAckRecord_t AckWaitList[MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME]; + +AWS_IoT_Client *pMqttClient; + +char myThingName[MAX_SIZE_OF_THING_NAME]; +char mqttClientID[MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES]; + +char shadowDeltaTopic[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + +#define MAX_TOPICS_AT_ANY_GIVEN_TIME 2*MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME +SubscriptionRecord_t SubscriptionList[MAX_TOPICS_AT_ANY_GIVEN_TIME]; + +#define SUBSCRIBE_SETTLING_TIME 2 +char shadowRxBuf[SHADOW_MAX_SIZE_OF_RX_BUFFER]; + +static JsonTokenTable_t tokenTable[MAX_JSON_TOKEN_EXPECTED]; +static uint32_t tokenTableIndex = 0; +static bool deltaTopicSubscribedFlag = false; +uint32_t shadowJsonVersionNum = 0; +bool shadowDiscardOldDeltaFlag = true; + +// local helper functions +static void AckStatusCallback(AWS_IoT_Client *pClient, char *topicName, + uint16_t topicNameLen, IoT_Publish_Message_Params *params, void *pData); + +static void shadow_delta_callback(AWS_IoT_Client *pClient, char *topicName, + uint16_t topicNameLen, IoT_Publish_Message_Params *params, void *pData); + +static void topicNameFromThingAndAction(char *pTopic, const char *pThingName, ShadowActions_t action, + ShadowAckTopicTypes_t ackType); + +static int16_t getNextFreeIndexOfSubscriptionList(void); + +static void unsubscribeFromAcceptedAndRejected(uint8_t index); + +void initDeltaTokens(void) { + uint32_t i; + for(i = 0; i < MAX_JSON_TOKEN_EXPECTED; i++) { + tokenTable[i].isFree = true; + } + tokenTableIndex = 0; + deltaTopicSubscribedFlag = false; +} + +IoT_Error_t registerJsonTokenOnDelta(jsonStruct_t *pStruct) { + + IoT_Error_t rc = AWS_SUCCESS; + + if(!deltaTopicSubscribedFlag) { + snprintf(shadowDeltaTopic, MAX_SHADOW_TOPIC_LENGTH_BYTES, "$aws/things/%s/shadow/update/delta", myThingName); + rc = aws_iot_mqtt_subscribe(pMqttClient, shadowDeltaTopic, (uint16_t) strlen(shadowDeltaTopic), QOS0, + shadow_delta_callback, NULL); + deltaTopicSubscribedFlag = true; + } + + if(tokenTableIndex >= MAX_JSON_TOKEN_EXPECTED) { + return FAILURE; + } + + tokenTable[tokenTableIndex].pKey = pStruct->pKey; + tokenTable[tokenTableIndex].callback = pStruct->cb; + tokenTable[tokenTableIndex].pStruct = pStruct; + tokenTable[tokenTableIndex].isFree = false; + tokenTableIndex++; + + return rc; +} + +static int16_t getNextFreeIndexOfSubscriptionList(void) { + uint8_t i; + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + if(SubscriptionList[i].isFree) { + SubscriptionList[i].isFree = false; + return i; + } + } + return -1; +} + +static void topicNameFromThingAndAction(char *pTopic, const char *pThingName, ShadowActions_t action, + ShadowAckTopicTypes_t ackType) { + + char actionBuf[10]; + char ackTypeBuf[10]; + + if(SHADOW_GET == action) { + strncpy(actionBuf, "get", 10); + } else if(SHADOW_UPDATE == action) { + strncpy(actionBuf, "update", 10); + } else if(SHADOW_DELETE == action) { + strncpy(actionBuf, "delete", 10); + } + + if(SHADOW_ACCEPTED == ackType) { + strncpy(ackTypeBuf, "accepted", 10); + } else if(SHADOW_REJECTED == ackType) { + strncpy(ackTypeBuf, "rejected", 10); + } + + if(SHADOW_ACTION == ackType) { + snprintf(pTopic, MAX_SHADOW_TOPIC_LENGTH_BYTES, "$aws/things/%s/shadow/%s", pThingName, actionBuf); + } else { + snprintf(pTopic, MAX_SHADOW_TOPIC_LENGTH_BYTES, "$aws/things/%s/shadow/%s/%s", pThingName, actionBuf, + ackTypeBuf); + } +} + +static bool isValidShadowVersionUpdate(const char *pTopicName) { + if(strstr(pTopicName, myThingName) != NULL && + ((strstr(pTopicName, "get/accepted") != NULL) || + (strstr(pTopicName, "delta") != NULL))) { + return true; + } + return false; +} + +static void AckStatusCallback(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) { + int32_t tokenCount; + uint8_t i; + void *pJsonHandler = NULL; + char temporaryClientToken[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE]; + + IOT_UNUSED(pClient); + IOT_UNUSED(topicNameLen); + IOT_UNUSED(pData); + + if(params->payloadLen >= SHADOW_MAX_SIZE_OF_RX_BUFFER) { + IOT_WARN("Payload larger than RX Buffer"); + return; + } + + memcpy(shadowRxBuf, params->payload, params->payloadLen); + shadowRxBuf[params->payloadLen] = '\0'; // jsmn_parse relies on a string + + if(!isJsonValidAndParse(shadowRxBuf, SHADOW_MAX_SIZE_OF_RX_BUFFER, pJsonHandler, &tokenCount)) { + IOT_WARN("Received JSON is not valid"); + return; + } + + if(isValidShadowVersionUpdate(topicName)) { + uint32_t tempVersionNumber = 0; + if(extractVersionNumber(shadowRxBuf, pJsonHandler, tokenCount, &tempVersionNumber)) { + if(tempVersionNumber > shadowJsonVersionNum) { + shadowJsonVersionNum = tempVersionNumber; + } + } + } + + if(extractClientToken(shadowRxBuf, SHADOW_MAX_SIZE_OF_RX_BUFFER, temporaryClientToken, MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE)) { + for(i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) { + if(!AckWaitList[i].isFree) { + if(strcmp(AckWaitList[i].clientTokenID, temporaryClientToken) == 0) { + Shadow_Ack_Status_t status = SHADOW_ACK_REJECTED; + if(strstr(topicName, "accepted") != NULL) { + status = SHADOW_ACK_ACCEPTED; + } else if(strstr(topicName, "rejected") != NULL) { + status = SHADOW_ACK_REJECTED; + } + if(status == SHADOW_ACK_ACCEPTED || status == SHADOW_ACK_REJECTED) { + if(AckWaitList[i].callback != NULL) { + AckWaitList[i].callback(AckWaitList[i].thingName, AckWaitList[i].action, status, + shadowRxBuf, AckWaitList[i].pCallbackContext); + } + unsubscribeFromAcceptedAndRejected(i); + AckWaitList[i].isFree = true; + return; + } + } + } + } + } +} + +static int16_t findIndexOfSubscriptionList(const char *pTopic) { + uint8_t i; + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + if(!SubscriptionList[i].isFree) { + if((strcmp(pTopic, SubscriptionList[i].Topic) == 0)) { + return i; + } + } + } + return -1; +} + +static void unsubscribeFromAcceptedAndRejected(uint8_t index) { + + char TemporaryTopicNameAccepted[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + char TemporaryTopicNameRejected[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + IoT_Error_t ret_val = AWS_SUCCESS; + + int16_t indexSubList; + + topicNameFromThingAndAction(TemporaryTopicNameAccepted, AckWaitList[index].thingName, AckWaitList[index].action, + SHADOW_ACCEPTED); + topicNameFromThingAndAction(TemporaryTopicNameRejected, AckWaitList[index].thingName, AckWaitList[index].action, + SHADOW_REJECTED); + + indexSubList = findIndexOfSubscriptionList(TemporaryTopicNameAccepted); + if((indexSubList >= 0)) { + if(!SubscriptionList[indexSubList].isSticky && (SubscriptionList[indexSubList].count == 1)) { + ret_val = aws_iot_mqtt_unsubscribe(pMqttClient, TemporaryTopicNameAccepted, + (uint16_t) strlen(TemporaryTopicNameAccepted)); + if(ret_val == AWS_SUCCESS) { + SubscriptionList[indexSubList].isFree = true; + } + } else if(SubscriptionList[indexSubList].count > 1) { + SubscriptionList[indexSubList].count--; + } + } + + indexSubList = findIndexOfSubscriptionList(TemporaryTopicNameRejected); + if((indexSubList >= 0)) { + if(!SubscriptionList[indexSubList].isSticky && (SubscriptionList[indexSubList].count == 1)) { + ret_val = aws_iot_mqtt_unsubscribe(pMqttClient, TemporaryTopicNameRejected, + (uint16_t) strlen(TemporaryTopicNameRejected)); + if(ret_val == AWS_SUCCESS) { + SubscriptionList[indexSubList].isFree = true; + } + } else if(SubscriptionList[indexSubList].count > 1) { + SubscriptionList[indexSubList].count--; + } + } +} + +void initializeRecords(AWS_IoT_Client *pClient) { + uint8_t i; + for(i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) { + AckWaitList[i].isFree = true; + } + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + SubscriptionList[i].isFree = true; + SubscriptionList[i].count = 0; + SubscriptionList[i].isSticky = false; + } + + pMqttClient = pClient; +} + +bool isSubscriptionPresent(const char *pThingName, ShadowActions_t action) { + + uint8_t i = 0; + bool isAcceptedPresent = false; + bool isRejectedPresent = false; + char TemporaryTopicNameAccepted[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + char TemporaryTopicNameRejected[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + + topicNameFromThingAndAction(TemporaryTopicNameAccepted, pThingName, action, SHADOW_ACCEPTED); + topicNameFromThingAndAction(TemporaryTopicNameRejected, pThingName, action, SHADOW_REJECTED); + + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + if(!SubscriptionList[i].isFree) { + if((strcmp(TemporaryTopicNameAccepted, SubscriptionList[i].Topic) == 0)) { + isAcceptedPresent = true; + } else if((strcmp(TemporaryTopicNameRejected, SubscriptionList[i].Topic) == 0)) { + isRejectedPresent = true; + } + } + } + + if(isRejectedPresent && isAcceptedPresent) { + return true; + } + + return false; +} + +IoT_Error_t subscribeToShadowActionAcks(const char *pThingName, ShadowActions_t action, bool isSticky) { + IoT_Error_t ret_val = AWS_SUCCESS; + + bool clearBothEntriesFromList = true; + int16_t indexAcceptedSubList = 0; + int16_t indexRejectedSubList = 0; + awsTimer subSettlingtimer; + indexAcceptedSubList = getNextFreeIndexOfSubscriptionList(); + indexRejectedSubList = getNextFreeIndexOfSubscriptionList(); + + if(indexAcceptedSubList >= 0 && indexRejectedSubList >= 0) { + topicNameFromThingAndAction(SubscriptionList[indexAcceptedSubList].Topic, pThingName, action, SHADOW_ACCEPTED); + ret_val = aws_iot_mqtt_subscribe(pMqttClient, SubscriptionList[indexAcceptedSubList].Topic, + (uint16_t) strlen(SubscriptionList[indexAcceptedSubList].Topic), QOS0, + AckStatusCallback, NULL); + if(ret_val == AWS_SUCCESS) { + SubscriptionList[indexAcceptedSubList].count = 1; + SubscriptionList[indexAcceptedSubList].isSticky = isSticky; + topicNameFromThingAndAction(SubscriptionList[indexRejectedSubList].Topic, pThingName, action, + SHADOW_REJECTED); + ret_val = aws_iot_mqtt_subscribe(pMqttClient, SubscriptionList[indexRejectedSubList].Topic, + (uint16_t) strlen(SubscriptionList[indexRejectedSubList].Topic), QOS0, + AckStatusCallback, NULL); + if(ret_val == AWS_SUCCESS) { + SubscriptionList[indexRejectedSubList].count = 1; + SubscriptionList[indexRejectedSubList].isSticky = isSticky; + clearBothEntriesFromList = false; + + // wait for SUBSCRIBE_SETTLING_TIME seconds to let the subscription take effect + init_timer(&subSettlingtimer); + countdown_sec(&subSettlingtimer, SUBSCRIBE_SETTLING_TIME); + while(!has_timer_expired(&subSettlingtimer)); + + } + } + } + + if(clearBothEntriesFromList) { + if(indexAcceptedSubList >= 0) { + SubscriptionList[indexAcceptedSubList].isFree = true; + + if(SubscriptionList[indexAcceptedSubList].count == 1) { + aws_iot_mqtt_unsubscribe(pMqttClient, SubscriptionList[indexAcceptedSubList].Topic, + (uint16_t) strlen(SubscriptionList[indexAcceptedSubList].Topic)); + } + } + if(indexRejectedSubList >= 0) { + SubscriptionList[indexRejectedSubList].isFree = true; + } + + } + + return ret_val; +} + +void incrementSubscriptionCnt(const char *pThingName, ShadowActions_t action, bool isSticky) { + char TemporaryTopicNameAccepted[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + char TemporaryTopicNameRejected[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + uint8_t i; + topicNameFromThingAndAction(TemporaryTopicNameAccepted, pThingName, action, SHADOW_ACCEPTED); + topicNameFromThingAndAction(TemporaryTopicNameRejected, pThingName, action, SHADOW_REJECTED); + + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + if(!SubscriptionList[i].isFree) { + if((strcmp(TemporaryTopicNameAccepted, SubscriptionList[i].Topic) == 0) + || (strcmp(TemporaryTopicNameRejected, SubscriptionList[i].Topic) == 0)) { + SubscriptionList[i].count++; + SubscriptionList[i].isSticky = isSticky; + } + } + } +} + +IoT_Error_t publishToShadowAction(const char *pThingName, ShadowActions_t action, const char *pJsonDocumentToBeSent) { + IoT_Error_t ret_val = AWS_SUCCESS; + char TemporaryTopicName[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + IoT_Publish_Message_Params msgParams; + + if(NULL == pThingName || NULL == pJsonDocumentToBeSent) { + return NULL_VALUE_ERROR; + } + + topicNameFromThingAndAction(TemporaryTopicName, pThingName, action, SHADOW_ACTION); + + msgParams.qos = QOS0; + msgParams.isRetained = 0; + msgParams.payloadLen = strlen(pJsonDocumentToBeSent); + msgParams.payload = (char *) pJsonDocumentToBeSent; + ret_val = aws_iot_mqtt_publish(pMqttClient, TemporaryTopicName, (uint16_t) strlen(TemporaryTopicName), &msgParams); + + return ret_val; +} + +bool getNextFreeIndexOfAckWaitList(uint8_t *pIndex) { + uint8_t i; + bool rc = false; + + if(NULL == pIndex) { + return false; + } + + for(i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) { + if(AckWaitList[i].isFree) { + *pIndex = i; + rc = true; + break; + } + } + + return rc; +} + +void addToAckWaitList(uint8_t indexAckWaitList, const char *pThingName, ShadowActions_t action, + const char *pExtractedClientToken, fpActionCallback_t callback, void *pCallbackContext, + uint32_t timeout_seconds) { + AckWaitList[indexAckWaitList].callback = callback; + memcpy(AckWaitList[indexAckWaitList].clientTokenID, pExtractedClientToken, MAX_SIZE_CLIENT_ID_WITH_SEQUENCE); + memcpy(AckWaitList[indexAckWaitList].thingName, pThingName, MAX_SIZE_OF_THING_NAME); + AckWaitList[indexAckWaitList].pCallbackContext = pCallbackContext; + AckWaitList[indexAckWaitList].action = action; + init_timer(&(AckWaitList[indexAckWaitList].timer)); + countdown_sec(&(AckWaitList[indexAckWaitList].timer), timeout_seconds); + AckWaitList[indexAckWaitList].isFree = false; +} + +void HandleExpiredResponseCallbacks(void) { + uint8_t i; + for(i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) { + if(!AckWaitList[i].isFree) { + if(has_timer_expired(&(AckWaitList[i].timer))) { + if(AckWaitList[i].callback != NULL) { + AckWaitList[i].callback(AckWaitList[i].thingName, AckWaitList[i].action, SHADOW_ACK_TIMEOUT, + shadowRxBuf, AckWaitList[i].pCallbackContext); + } + AckWaitList[i].isFree = true; + unsubscribeFromAcceptedAndRejected(i); + } + } + } +} + +static void shadow_delta_callback(AWS_IoT_Client *pClient, char *topicName, + uint16_t topicNameLen, IoT_Publish_Message_Params *params, void *pData) { + int32_t tokenCount; + uint32_t i = 0; + void *pJsonHandler = NULL; + int32_t DataPosition; + uint32_t dataLength; + uint32_t tempVersionNumber = 0; + + FUNC_ENTRY; + + IOT_UNUSED(pClient); + IOT_UNUSED(topicName); + IOT_UNUSED(topicNameLen); + IOT_UNUSED(pData); + + if(params->payloadLen >= SHADOW_MAX_SIZE_OF_RX_BUFFER) { + IOT_WARN("Payload larger than RX Buffer"); + return; + } + + memcpy(shadowRxBuf, params->payload, params->payloadLen); + shadowRxBuf[params->payloadLen] = '\0'; // jsmn_parse relies on a string + + if(!isJsonValidAndParse(shadowRxBuf, SHADOW_MAX_SIZE_OF_RX_BUFFER, pJsonHandler, &tokenCount)) { + IOT_WARN("Received JSON is not valid"); + return; + } + + if(shadowDiscardOldDeltaFlag) { + if(extractVersionNumber(shadowRxBuf, pJsonHandler, tokenCount, &tempVersionNumber)) { + if(tempVersionNumber > shadowJsonVersionNum) { + shadowJsonVersionNum = tempVersionNumber; + } else { + IOT_WARN("Old Delta Message received - Ignoring rx: %ld local: %ld", tempVersionNumber, + shadowJsonVersionNum); + return; + } + } + } + + for(i = 0; i < tokenTableIndex; i++) { + if(!tokenTable[i].isFree) { + if(isJsonKeyMatchingAndUpdateValue(shadowRxBuf, pJsonHandler, tokenCount, + (jsonStruct_t *) tokenTable[i].pStruct, &dataLength, &DataPosition)) { + if(tokenTable[i].callback != NULL) { + tokenTable[i].callback(shadowRxBuf + DataPosition, dataLength, + (jsonStruct_t *) tokenTable[i].pStruct); + } + } + } + } +} + +#ifdef __cplusplus +} +#endif