สารบัญ:

SmartBin: 8 ขั้นตอน
SmartBin: 8 ขั้นตอน

วีดีโอ: SmartBin: 8 ขั้นตอน

วีดีโอ: SmartBin: 8 ขั้นตอน
วีดีโอ: Smart Bin By NodemcuESP8266 สอนทำถังขยะอัตโนมัติด้วย Nodemcu ESP8266 2024, พฤศจิกายน
Anonim
SmartBin
SmartBin

Este é um projeto para um sistema inteligente de coletas, no qual os caminhões de lixo recebem dados das lixeiras, identificando a quantidade de lixo presente em cada uma delas, e uma rota de coletacoms traçupa. ข้อมูลทั่วไป

Para montar este projeto, จำเป็น:

  • NodeMCU
  • เซนเซอร์ Ultrassônico de Distancia
  • ไกซา เด ปาเปเลา
  • โปรโตบอร์ด
  • Cabos
  • Dispositivo Android

ขั้นตอนที่ 1: Conectando O Sensor

Primeiramente, vamos efetuar a conexão entre o เซ็นเซอร์ ultrassônico e o NODEMCU Para tanto, vamos conectar เป็น portas trigger e echo do sensor nas portas D4 e D3 do NodeMCU:

// กำหนดหมายเลขพิน #define pino_trigger 2 //D4

#define pino_echo 0 //D3

สำหรับตัวเลือกอื่นๆ เช่น leitura dos dados do sensor, foi seguido o tutorial elaborado pelo FilipeFlop, disponível aqui.

ลอย cmMsec, inMsec;

microsec ยาว = Ultrasonic.timing ();

cmMsec = Ultrasonic.convert (ไมโครเซค, อัลตราโซนิก::CM);

inMsec = ultrasonic.convert (microsec, Ultrasonic::IN);

//Exibe Informacoes ไม่มีจอภาพแบบอนุกรม

Serial.print("Distancia em cm: ");

Serial.print(cmMsec);

Serial.print(" - Distancia em polegadas: ");

Serial.println (inMsec);

ข้อมูลสตริง = สตริง (cmMsec);

Serial.println (ข้อมูล);

ขั้นตอนที่ 2: Montando a Lixeira

Agora, vamos montar a lixeira inteligente. Precisaremos conectar o เซ็นเซอร์ ultrassônico no “teto” da lixeira Para o แบบอย่าง ใช้ um cabo e fita isolante Em seguida, temos que medir a distância inicial, para saber o valor para a lixeira วาเซีย No meu caso, ฝอย 26, 3ซม. Esse é o valor que viewerarmos สำหรับ uma lixeira vazia

สำหรับการจำลอง, visto que não possuo mais de um sensor ultrassônico, foi feito um algoritmo para salvar randomicamente a distancia lida em 4 lixeiras diferentes.

// Simulando 4 lixeiras

lixeiraID ยาว;

วงเป็นโมฆะ () {

lixeiraID = สุ่ม (1, 5);

}

ขั้นตอนที่ 3: อัปโหลด Para a Nuvem

อโกรา, precisamos enviar estes dados para a nuvem. Eu escolhi o ThingSpeak, จากที่คุ้นเคย com o mesmo Primeiramente, é necessário criar um novo canal, recebendo 4 parâmetros, อ้างอิง ao volume de cada lixeira

เชื่อมต่อ aplicação com o ThingSpeak, é necessário salvar o número da API do canal criado. Siga os passos อธิบายว่าไม่มีไซต์อย่างเป็นทางการ

แอปพลิเคชันที่ใช้, vamos utilizar a biblioteca ESP8266WiFi.h para efetuar conexão com o ThingSpeak, และ transferir os dados

Primeiramente, uma função para efetuar conexão com a rede (defina previamente duas variáveis, ssid e pass, contendo o identificador e a senha de sua rede).

เป็นโมฆะ connectWifi (){

Serial.print("กำลังเชื่อมต่อกับ "+ *ssid);

WiFi.begin(ssid, ผ่าน);

ในขณะที่ (WiFi.status () != WL_CONNECTED) {

ล่าช้า (500);

Serial.print(".");

}

Serial.println("");

Serial.print("ติดต่อและทำซ้ำ");

Serial.println(ssid);

Serial.print("IP: ");

Serial.println(WiFi.localIP());

}

Durante o การติดตั้ง, tentamos efetuar a conexão com a rede.

การตั้งค่าเป็นโมฆะ () {

Serial.begin(9600);

Serial.println("Lendo dados ทำเซ็นเซอร์…");

//Conectando ao Wi-Fi

เชื่อมต่อ WiFi();

}

E, para enviar os dados para o ThingSpeak, basta abrir uma conexão HTTP padrão, passando o número da API e os parâmetros

เป็นโมฆะ sendDataTS (ลอย cmMsec, รหัสแบบยาว){

if (client.connect (เซิร์ฟเวอร์ 80)) {

Serial.println("Enviando dados สำหรับ ThingSpeak ");

สตริง postStr = apiKey;

postStr += "&field";

postStr += id;

postStr += "=";

postStr += สตริง (cmMsec);

postStr += "\r\n\r\n";

Serial.println (postStr);

client.print( POST /update

client.print("โฮสต์: api.thingspeak.com\n");

client.print("การเชื่อมต่อ: ปิด\n");

client.print("X-THINGSPEAKAPIKEY: " + apiKey + "\n");

client.print("Content-Type: application/x-www-form-urlencoded\n");

client.print("เนื้อหา-ความยาว: ");

client.print(postStr.length());

client.print("\n\n");

client.print(postStr);

ล่าช้า (1000);

}

client.stop();

}

O primeiro parâmetro สอดคล้อง à distância em centímetros encontrada pelo sensor ultrassônico. O segundo parâmetro é o ID da lixeira que foi lida (que foi gerado randomicamente, มากกว่า 1 a 4).

O ID da lixeira ให้บริการ também para identificar para qual campo será feito o upload do valor lido.

ขั้นตอนที่ 4: Recuperando Dados Do ThingSpeak

O ThingSpeak อนุญาต efetuar leitura dos dados do seu canal, através de um serviço retornando um JSON ในฐานะที่ต่างกัน opções para leitura do feed do seu canal estão descritas aqui:

www.mathworks.com/help/thingspeak/get-a-ch…

Neste projeto, optou-se por ler diretamente os dados de cada campo. ที่อยู่ URL สำหรับ este cenário é:

api.thingspeak.com/channels/CHANNEL_ID/fields/FIELD_NUMBER/last.json?api_key=API_KEY&status=true

Cada campo está descrito ไม่มีลิงก์แจ้งข้อมูลเบื้องต้น Os mais สำคัญสำหรับ o projeto são:

  • CHANNEL_ID: número do seu canal
  • FIELD_NUMBER: o número do campo
  • API_KEY: a chave de API do seu canal

Esta é a URL สำหรับใช้งาน Android, ระบบปฏิบัติการสำหรับกู้คืน ThingSpeak.

ขั้นตอนที่ 5: Criando a Aplicação Android

ไม่มี Android Studio ร้องเลยสำหรับ Android Para o correto funcionameto da aplicação, é necessário กำหนดค่าเป็น permissões abaixo no AndroidManifest

ประโยชน์ใช้สอยบน Google Maps, จำเป็น pegar uma chave junto ao Google Siga os passos อธิบายว่าไม่มีลิงก์ Obter chave de API

Uma vez com a chave, você deve também กำหนดค่า-la na aplicação

คีย์ API สำหรับ API ที่ใช้ Google Maps ถูกกำหนดให้เป็นทรัพยากรสตริง

(ดูไฟล์ "res/values/google_maps_api.xml")

โปรดทราบว่าคีย์ API เชื่อมโยงกับคีย์การเข้ารหัสที่ใช้ในการลงนาม APK คุณต้องมีคีย์ API ที่แตกต่างกันสำหรับคีย์การเข้ารหัสแต่ละคีย์ รวมถึงคีย์การวางจำหน่ายที่ใช้ในการลงนาม APK สำหรับการเผยแพร่ คุณสามารถกำหนดคีย์สำหรับการดีบักและรีลีสเป้าหมายใน src/debug/ และ src/release/

<meta-data

android:name="com.google.android.geo. API_KEY"

android:value="@string/google_maps_key" />

การกำหนดค่าที่สมบูรณ์ está mo arquivo AndroidManifest anexado ao projeto

NS

ขั้นตอนที่ 6: Recuperando O Feed ไม่มี Android

หลักการของ Android, MainActivity, crie 4 variáveis para identificar cada um dos canais do ThingSpeak a serem lidos:

url_a สตริงส่วนตัว = "https://api.thingspeak.com/channels/429823/fields/1/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; url_b สตริงส่วนตัว = "https://api.thingspeak.com/channels/429823/fields/2/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; url_c สตริงส่วนตัว = "https://api.thingspeak.com/channels/429823/fields/3/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; url_d สตริงส่วนตัว = "https://api.thingspeak.com/channels/429823/fields/4/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true";

สำหรับ leitura dos dados, iremos utilizar uma classe do Android específica, chamada JSONObject Mais uma vez, vamos criar um objeto para cada URL:

JSONวัตถุตอบสนองLixeiraA; JSONวัตถุตอบสนองLixeiraB; JSONวัตถุตอบสนองLixeiraC; JSONวัตถุตอบสนองLixeiraD;

ใช้ร่วมกันเป็น urls, vamos usar criar uma classe auxiliar, chamada HttpJsonParser Esta classe será responsável por abrir uma conexão com um URL, efetuar leitura dos dados encontrados, และ retornar o objeto JSON montado.

JSONObject makeHttpRequest สาธารณะ (String url, String method, Map params) {

ลอง {

ตัวสร้าง Uri. Builder = Uri. Builder ใหม่ (); URL urlObj; สตริงที่เข้ารหัสพารามิเตอร์ = ""; if (params != null) { สำหรับ (รายการ Map. Entry: params.entrySet()) { builder.appendQueryParameter(entry.getKey(), entry.getValue()); } } if (builder.build().getEncodedQuery() != null) { encodedParams = builder.build().getEncodedQuery();

}

ถ้า ("GET".equals (เมธอด)) { url = url + "?" + เข้ารหัสพารามิเตอร์; urlObj = URL ใหม่ (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod(เมธอด);

} อื่น {

urlObj = URL ใหม่ (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod(เมธอด); urlConnection.setRequestProperty("ประเภทเนื้อหา", "แอปพลิเคชัน/x-www-form-urlencoded"); urlConnection.setRequestProperty("เนื้อหา-Length", String.valueOf(encodedParams.getBytes().length)); urlConnection.getOutputStream().write(encodedParams.getBytes()); } //เชื่อมต่อกับเซิร์ฟเวอร์ urlConnection.connect(); //อ่านคำตอบคือ = urlConnection.getInputStream(); ตัวอ่าน BufferedReader = BufferedReader ใหม่ (InputStreamReader ใหม่ (คือ)); StringBuilder sb = ใหม่ StringBuilder (); เส้นสาย;

//แยกวิเคราะห์คำตอบ

ในขณะที่ ((line = reader.readLine()) != null) { sb.append(line + "\n"); } is.close(); json = sb.toString(); //แปลงการตอบสนองต่อวัตถุ JSON jObj = ใหม่ JSONObject(json);

} จับ (UnsupportedEncodingException จ) {

e.printStackTrace(); } จับ (ProtocolException จ) { e.printStackTrace (); } จับ (IOException จ) { e.printStackTrace (); } catch (JSONException e) { Log.e ("JSON Parser", "ข้อผิดพลาดในการแยกวิเคราะห์ข้อมูล" + e.toString ()); } catch (ข้อยกเว้น e) { Log.e ("ข้อยกเว้น", "ข้อผิดพลาดในการแยกวิเคราะห์ข้อมูล" + e.toString ()); }

// ส่งคืนวัตถุ JSON

ส่งคืน jObj;

}

}

หลักการสำคัญ, vamos efetuar a chamada às urls de forma assíncrona, escrevendo este código dentro do método doInBackground.

@Override ป้องกัน String doInBackground(String… params) { HttpJsonParser jsonParser = new

responseLixeiraA = jsonParser.makeHttpRequest(url_a, "GET", null);

responseLixeiraB = jsonParser.makeHttpRequest(url_b, "GET", null); responseLixeiraC = jsonParser.makeHttpRequest(url_c, "GET", null); responseLixeiraD = jsonParser.makeHttpRequest(url_d, "GET", null);

คืนค่า null;}

Quando o método doInBackgroundé encerrado, o controle de execução do Android passa para o método onPostExecute. Neste método, vamos criar os objetos Lixeira, e popular com os dados recuperados do ThingSpeak:

ป้องกันโมฆะ onPostExecute (ผลลัพธ์สตริง) { pDialog.dismiss (); runOnUiThread (รันใหม่ได้ () { โมฆะสาธารณะรัน () {

//ListView listView =(ListView)findViewById(R.id.feedList); //ListView listView =(ListView)findViewById(R.id.feedList);

ดู mainView =(ดู)findViewById(R.id.activity_main); ถ้า (สำเร็จ == 1) { ลอง {// Cria feedDetail para cada lixeira Lixeira feedDetails1 = ใหม่ Lixeira (); Lixeira feedDetails2 = ใหม่ Lixeira (); Lixeira feedDetails3 = ใหม่ Lixeira (); Lixeira feedDetails4 = ใหม่ Lixeira ();

feedDetails1.setId('A');

feedDetails1.setPesoLixo(Double.parseDouble(การตอบสนองLixeiraA.getString(KEY_FIELD1))); feedDetails1.setVolumeLixo(Double.parseDouble(การตอบสนองLixeiraA.getString(KEY_FIELD1)));

feedDetails2.setId('B');

feedDetails2.setPesoLixo(Double.parseDouble(การตอบสนองLixeiraB.getString(KEY_FIELD2)))); feedDetails2.setVolumeLixo(Double.parseDouble(การตอบสนองLixeiraB.getString(KEY_FIELD2))));

feedDetails3.setId('C');

feedDetails3.setPesoLixo(Double.parseDouble(การตอบสนองLixeiraC.getString(KEY_FIELD3))); feedDetails3.setVolumeLixo(Double.parseDouble(การตอบสนองLixeiraC.getString(KEY_FIELD3)));

feedDetails4.setId('D');

feedDetails4.setPesoLixo(Double.parseDouble(การตอบสนองLixeiraD.getString(KEY_FIELD4)))); feedDetails4.setVolumeLixo(Double.parseDouble(responseLixeiraD.getString(KEY_FIELD4))));

feedList.add(feedDetails1);

feedList.add(feedDetails2); feedList.add(feedDetails3); feedList.add(feedDetails4);

//Calcula dados das lixeiras

เครื่องคิดเลข SmartBinService = ใหม่ SmartBinService(); เครื่องคิดเลข.montaListaLixeiras(feedList);

//ส่วนประกอบRecupera

TextView createDate = (TextView) mainView.findViewById (R.id.date); ListView listaDeLixeiras = (ListView) findViewById (R.id.lista); adapter.addAll(feedList);

//ข้อมูลจริง

วันที่ currentTime = Calendar.getInstance().getTime(); SimpleDateFormat simpleDate = ใหม่ SimpleDateFormat("dd/MM/yyyy"); สตริง currentDate = simpleDate.format (currentTime); createDate.setText(KEY_DATE + currentDate + " "); listaDeLixeiras.setAdapter (อะแดปเตอร์);

} จับ (JSONException จ) {

e.printStackTrace(); }

} อื่น {

Toast.makeText(MainActivity.this, "มีข้อผิดพลาดเกิดขึ้นขณะโหลดข้อมูล", Toast. LENGTH_LONG).show();

}

} }); }

อโกรา, na tela inicial do aplicativo, serão listados os dados de cada lixeira

ขั้นตอนที่ 7: Mostrando No Mapa

โมสตรันโด โน มาปา
โมสตรันโด โน มาปา

Ainda na atividade principal, vamos adicionar uma ação a ser relacionada ao botão Mapa, na tela เริ่มต้น

/** เรียกเมื่อผู้ใช้แตะปุ่ม Mapa */ โมฆะสาธารณะ openMaps (ดูมุมมอง) { เจตนาเจตนา = เจตนาใหม่ (สิ่งนี้ LixeiraMapsActivity.class);

//Passa a lista de lixeiras

บันเดิลบันเดิล = บันเดิลใหม่ (); bundle.putParcelableArrayList("lixeiras", feedList); เจตนา.putExtras(มัด);

startActivity(เจตนา);

}

ไม่มี mapa, temos três atividades a executar:

  1. marcar a posição atual do caminha de lixo
  2. marcar os pontos ผู้สื่อข่าว a cada lixeira no mapa
  3. traçar a rota entre os pontos

Para executar os passos acima, vamos ใช้ API Google Directions Para desenhar เป็น rotas, foram seguidos os passos do tutorial การวาดเส้นทางเส้นทางการขับขี่ระหว่างสองสถานที่โดยใช้ Google Directions ใน Google Map Android API V2

Primeiro, vamos criar localidades para cada um dos pontos que desejamos marcar:

//สถานที่

LatLng ส่วนตัวปัจจุบัน;

LatLng ส่วนตัว lixeiraA; LatLng ส่วนตัว lixeiraB; LatLng ส่วนตัว lixeiraC; ส่วนตัว LatLng lixeiraD;.

สำหรับ adicionar a posição atual no mapa, foi criado o método:

ตรวจสอบโมฆะส่วนตัว checkLocationandAddToMap () { // ตรวจสอบว่าผู้ใช้ได้รับอนุญาตหรือไม่หาก (ActivityCompat.checkSelfPermission (นี้ android. Manifest.permission. ACCESS_FINE_LOCATION) != PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (นี้ android. Manifest.permission ACCESS_COARSE_LOCATION) != PackageManager. PERMISSION_GRANTED) { // การขอสิทธิ์อนุญาตตำแหน่ง ActivityCompat.requestPermissions (นี่ สตริงใหม่ {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); กลับ; }

//ดึงตำแหน่งที่รู้จักล่าสุดโดยใช้ Fus

ตำแหน่งที่ตั้ง = LocationServices. FusedLocationApi.getLastLocation (googleApiClient);

//MarkerOptions ใช้เพื่อสร้าง Marker ใหม่ คุณสามารถระบุตำแหน่ง ชื่อ ฯลฯ ด้วย MarkerOptions

this.current = ใหม่ LatLng(location.getLatitude(), location.getLongitude()); MarkerOptions markerOptions = MarkerOptions ใหม่ ().position(current).title("Posição atual");

//เพิ่มเครื่องหมายที่สร้างบนแผนที่ โดยย้ายกล้องไปที่ตำแหน่ง

markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory. HUE_GREEN)); System.out.println("+++++++++++++ Passei aqui! +++++++++++++"); mMap.addMarker (markerOptions);

// ย้ายกล้องไปยังตำแหน่งทันทีด้วยการซูม 15

mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(ปัจจุบัน, 15));

// ซูมเข้า ทำให้กล้องเคลื่อนไหว

mMap.animateCamera(CameraUpdateFactory.zoomTo(14), 2000, null);

}

Em seguida, para cada lixeira, foram criados métodos similares ao abaixo:

โมฆะส่วนตัว addBinALocation () {// ตรวจสอบว่าผู้ใช้ได้รับอนุญาตหรือไม่หาก (ActivityCompat.checkSelfPermission (นี้ android. Manifest.permission. ACCESS_FINE_LOCATION) != PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (นี้ android. Manifest.permission ACCESS_COARSE_LOCATION) != PackageManager. PERMISSION_GRANTED) { // การขอสิทธิ์อนุญาตตำแหน่ง ActivityCompat.requestPermissions (นี่ สตริงใหม่ {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); กลับ; }

// Praça da Estação

ละติจูดสองเท่า = -19.9159578; ลองจิจูดสองเท่า = -43.9387856; this.lixeiraA = LatLng ใหม่ (ละติจูด, ลองจิจูด);

MarkerOptions markerOptions = MarkerOptions ใหม่ ().position(lixeiraA).title("Lixeira A");

markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory. HUE_RED)); mMap.addMarker (markerOptions); }

ในฐานะที่เป็น posições de latitude e ลองจิจูด de cada lixeira foram recuperadas através do próprio Google Maps, e deixadas fixas no código Idealmente, estes valores ficariam salvos em um banco de dados (ตัวอย่าง Firebase) Será a primeira evolução deste projeto!

O ultimo passo agora é traçar เป็น rotas entre os pontos Para tal, um conceito muito importante, e que será utilizado neste projeto, são os Waypoints!

Foi criado um método para traçar a rota entre dois dados pontos:

สตริงส่วนตัว getDirectionsUrl (แหล่งกำเนิด LatLng ปลายทาง LatLng รายการจุดอ้างอิง) {

//ที่มาของเส้นทาง

สตริง str_origin = "origin="+origin.latitude+", "+origin.longitude;

// ปลายทางของเส้นทาง

สตริง str_dest = "destination="+dest.latitude+", "+dest.longitude;

//จุดแวะระหว่างทาง

//waypoints=optimize:true|-19.9227365, -43.9473546|-19.9168006, -43.9361124 String waypoints = "waypoints=optimize:true"; สำหรับ (จุด LatLng: waypointsList){ จุดอ้างอิง += "|" + point.latitude + "," + point.longitude; }

// เปิดใช้งานเซ็นเซอร์

เซ็นเซอร์สตริง = "เซ็นเซอร์ = เท็จ";

// การสร้างพารามิเตอร์ให้กับเว็บเซอร์วิส

พารามิเตอร์สตริง = str_origin+"&"+str_dest+"&"+เซ็นเซอร์ + "&" + จุดอ้างอิง;

// รูปแบบเอาต์พุต

เอาต์พุตสตริง = "json";

// การสร้าง url ไปยังบริการเว็บ

URL สตริง = "https://maps.googleapis.com/maps/api/directions/"+output+"?"+พารามิเตอร์; System.out.println("++++++++++++++ "+url);

ส่งคืน url;

}

E, por fim, juntando tudo no método principal da classe, onMapReady:

@แทนที่โมฆะสาธารณะ onMapReady (GoogleMap googleMap) { mMap = googleMap;

checkLocationandAddToMap();

ถ้า (lixeirasList.get(0).getVolumeLixo() > Lixeira. MIN_VOLUME_GARBAGE

|| lixeirasList.get(0).getPesoLixo()-10 > Lixeira. MIN_SIZE_GARBAGE){ addBinALocation(); } if (lixeirasList.get(1).getVolumeLixo() > Lixeira. MIN_VOLUME_GARBAGE || lixeirasList.get(1).getPesoLixo() > Lixeira. MIN_SIZE_GARBAGE){ addBinBLocation(); } if (lixeirasList.get(2).getVolumeLixo() > Lixeira. MIN_VOLUME_GARBAGE || lixeirasList.get(2).getPesoLixo() > Lixeira. MIN_SIZE_GARBAGE){ addBinCLocation(); } if (lixeirasList.get(3).getVolumeLixo() > Lixeira. MIN_VOLUME_GARBAGE || lixeirasList.get(3).getPesoLixo() > Lixeira. MIN_SIZE_GARBAGE){ addBinDLocation(); }

//วาดเส้นทาง

// รับ URL ไปยัง Google Directions API

รายการคะแนน = ใหม่ ArrayList(); points.add(lixeiraB); points.add(lixeiraC); points.add(lixeiraD);

String url = getDirectionsUrl(ปัจจุบัน, lixeiraA, คะแนน);

DownloadTask downloadTask = ใหม่ DownloadTask(); // เริ่มดาวน์โหลดข้อมูล json จาก Google Directions API downloadTask.execute(url); }

Aqui passamos apenas pelos pontos principais. O código completo do projeto será disponibilizado para Consulta. ได้

ขั้นตอนที่ 8: Conclusão

Este foi um projeto trabalhando conceitos de IoT, Mostrando uma das várias opções de conectar dispositivos através da nuvem, e efetuar tomada de decisões sem interferência humana direta Em anexo, segue um vídeo do projeto completo, สำหรับรูปภาพ, e os fontes das atividades criadas no Android.

แนะนำ: