Table des matières:

SmartBin : 8 étapes
SmartBin : 8 étapes

Vidéo: SmartBin : 8 étapes

Vidéo: SmartBin : 8 étapes
Vidéo: How to Pair XIAOMI Smart Band 8 with Android Phone 2024, Novembre
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 coleta traçada, com base up nas informações.

Para montar este projeto, é necessário:

  • NodeMCU
  • Capteur Ultrassônico de Distancia
  • Caixa de papelão
  • Protoboard
  • Cabos
  • Dispositif Android

Étape 1: Conectando O Sensor

Primeiramente, vamos efetuar a conexão entre o sensor ultrassônico e o NODEMCU. Para tanto, vamos conectar as portas trigger e echo do sensor nas portas D4 e D3 do NodeMCU:

// définit les numéros de broches #define pino_trigger 2 //D4

#define pino_echo 0 //D3

Para efetuar a leitura dos dados do sensor, foi seguido o tutorial elaborado pelo FilipeFlop, disponível aqui.

flottant cmMsec, inMsec;

long microsec = ultrasonic.timing();

cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM);

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

//Exibe informacoes pas de moniteur série

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

Serial.print(cmMsec);

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

Serial.println(inMsec);

Données de chaîne = Chaîne(cmMsec);

Serial.println(données);

Étape 2: Montando a Lixeira

Agora, vamos montar a lixeira inteligente. Precisaremos conectar o sensor ultrassônico no “teto” da lixeira. Para o exemplo, usei um cabo e fita isolante. Em seguida, temos que medir a distância inicial, para saber o valor para a lixeira vazia. No meu caso, foi de 26, 3cm. Esse é o valor que considerarmos para uma lixeira vazia.

Para simulação, visto que não possuo mais de um sensor ultrassônico, foi feito um algoritmo para salvar randomicamente a distance lida em 4 lixeiras diferentes.

//Simulando 4 lixeiras

longue lixeiraID;

boucle vide() {

lixeiraID = aléatoire(1, 5);

}

Étape 3: Télécharger Para a Nuvem

Agora, precisamos enviar estes dados para a nuvem. Eu escolhi o ThingSpeak, por familiaridade com o mesmo. Primeiramente, é necessário criar um novo canal, recebendo 4 parâmetros, referentes ao volume de cada lixeira.

Pará conectar a aplicação com o ThingSpeak, é necessário salvar o número da API do canal criado. Siga os passos descritos aucun site officiel.

De volta à aplicação, vamos utilizar a biblioteca ESP8266WiFi.h para efetuar conexão com o ThingSpeak, e transferir os dados.

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

void connectWifi(){

Serial.print("Connexion à "+ *ssid);

WiFi.begin(ssid, pass);

while (WiFi.status() != WL_CONNECTED) {

retard (500);

Serial.print(".");

}

Serial.println("");

Serial.print("Conectado na rede ");

Serial.println(ssid);

Serial.print("IP: ");

Serial.println(WiFi.localIP());

}

Durante o setup, tentamos efetuar a conexão com a rede.

void setup() {

Serial.begin(9600);

Serial.println("Lendo dados do sensor…");

// Connexion au Wi-Fi

connectWifi();

}

E, par enviar os dados para o ThingSpeak, basta ouvrir uma conexão HTTP padrão, passer o número da API e os paramètres.

void sendDataTS(float cmMsec, long id){

if (client.connect(serveur, 80)) {

Serial.println("Envoyer des données pour ThingSpeak ");

Chaîne postStr = apiKey;

postStr += "&champ";

postStr += id;

postStr += "=";

postStr += String(cmMsec);

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

Serial.println(postStr);

client.print( POST /mise à jour

client.print("Hôte: api.thingspeak.com\n");

client.print("Connexion: fermer\n");

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

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

client.print("Content-Length: ");

client.print(postStr.length());

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

client.print(postStr);

retard(1000);

}

client.stop();

}

O primeiro parâmetro corresponde à 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, um número de 1 a 4).

O ID da lixeira serve também para identificar para qual campo será feito o upload do valor lido.

Étape 4: Recuperando Dados Do ThingSpeak

O ThingSpeak permite efetuar leitura dos dados do seu canal, atraves de um serviço retornando um JSON. Comme diferentes 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. O padrão de URL para este cenário é:

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

Cada campo está descrito no link informado previamente. Les éléments les plus importants pour le projet sont:

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

Esta é une URL que será lida do aplicativa Android, para recuperar os dodos do ThingSpeak.

Étape 5: Criando a Aplicação Android

Pas d'Android Studio, crie um novo projeto Android. Para o correto funcionamento da aplicação, é necessário configurar as permitões abaixo no AndroidManifest.

Pour utiliser Google Maps, será necessário pegar uma chave junto ao Google. Siga os passos descritos no link Obter chave de API.

Uma vez com a chave, você deve também configurá-la na aplicação.

La clé API pour les API basées sur Google Maps est définie comme une ressource de chaîne.

(Voir le fichier "res/values/google_maps_api.xml").

Notez que la clé API est liée à la clé de chiffrement utilisée pour signer l'APK. Vous avez besoin d'une clé API différente pour chaque clé de chiffrement, y compris la clé de version qui est utilisée pour signer l'APK pour la publication. Vous pouvez définir les clés des cibles de débogage et de libération dans src/debug/ et src/release/.

<méta-données

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

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

Une configuration complète est à partir de l'arquivo AndroidManifest anexado ao projeto.

m

Étape 6: Recuperando O Feed No Android

Na atividade principal no Android, MainActivity, crie 4 variáveis para identificar cada um dos canais do ThingSpeak a serem lidos:

chaîne privée url_a = "https://api.thingspeak.com/channels/429823/fields/1/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; chaîne privée url_b = "https://api.thingspeak.com/channels/429823/fields/2/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; chaîne privée url_c = "https://api.thingspeak.com/channels/429823/fields/3/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; chaîne privée url_d = "https://api.thingspeak.com/channels/429823/fields/4/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true";

Para efetuar a leitura dos dados, iremos utilizar uma class do Android específica, chamada JSONObject. Mais uma vez, vamos criar um objeto para cada URL:

Réponse JSONObjectLixeiraA; Réponse JSONObjectLixeiraB; Réponse de l'objet JSONLixeiraC; Réponse de l'objet JSONLixeiraD;

Pour ouvrir un conexão com comme URL, vamos usar criar uma classe auxiliaire, chamada HttpJsonParser. Esta classe será responsável por ouvrir uma conexão com um URL, efetuar leitura dos dados encontrados, e retornar o objecto JSON montado.

public JSONObject makeHttpRequest (URL de chaîne, méthode de chaîne, paramètres de carte) {

essayer {

Uri. Builder builder = new Uri. Builder(); URL urlObj; Chaîne encodedParams = ""; if (params != null) { for (entrée Map. Entry: params.entrySet()) { builder.appendQueryParameter(entry.getKey(), entry.getValue()); } } if (builder.build().getEncodedQuery() != null) { encodedParams = builder.build().getEncodedQuery();

}

if ("GET".equals(method)) { url = url + "?" + paramètres encodés; urlObj = nouvelle URL(url); urlConnection = (HttpURLConnection) urlObj.openConnection(); urlConnection.setRequestMethod(method);

} autre {

urlObj = nouvelle URL(url); urlConnection = (HttpURLConnection) urlObj.openConnection(); urlConnection.setRequestMethod(method); urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); urlConnection.setRequestProperty("Content-Length", String.valueOf(encodedParams.getBytes().length)); urlConnection.getOutputStream().write(encodedParams.getBytes()); } //Connexion au serveur urlConnection.connect(); //Lire la réponse est = urlConnection.getInputStream(); Lecteur BufferedReader = new BufferedReader(nouveau InputStreamReader(is)); StringBuilder sb = new StringBuilder(); Ligne de chaîne;

//Analyser la réponse

while ((ligne = reader.readLine()) != null) { sb.append(ligne + "\n"); } est près(); json = sb.toString(); //Convertir la réponse en JSON Object jObj = new JSONObject(json);

} catch (UnsupportedEncodingException e) {

e.printStackTrace(); } catch (ProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (JSONException e) { Log.e("JSON Parser", "Error parsing data " + e.toString()); } catch (Exception e) { Log.e("Exception", "Erreur d'analyse des données " + e.toString()); }

// renvoie l'objet JSON

retourner jObj;

}

}

De volta a atividade principal, vamos efetuar a chamada às urls de forma assíncrona, escrevendo este código dentro do método doInBackground.

@Override protected 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);

renvoie 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:

protected void onPostExecute(String result) { pDialog.dismiss(); runOnUiThread(new Runnable() { public void run() {

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

Voir mainView =(View)findViewById(R.id.activity_main); if (success == 1) { try { //Cria feedDetail para cada lixeira Lixeira feedDetails1 = new Lixeira(); Lixeira feedDetails2 = new Lixeira(); Lixeira feedDetails3 = new Lixeira(); Lixeira feedDetails4 = new Lixeira();

feedDetails1.setId('A');

feedDetails1.setPesoLixo(Double.parseDouble(responseLixeiraA.getString(KEY_FIELD1))); feedDetails1.setVolumeLixo(Double.parseDouble(responseLixeiraA.getString(KEY_FIELD1)));

feedDetails2.setId('B');

feedDetails2.setPesoLixo(Double.parseDouble(responseLixeiraB.getString(KEY_FIELD2))); feedDetails2.setVolumeLixo(Double.parseDouble(responseLixeiraB.getString(KEY_FIELD2)));

feedDetails3.setId('C');

feedDetails3.setPesoLixo(Double.parseDouble(responseLixeiraC.getString(KEY_FIELD3))); feedDetails3.setVolumeLixo(Double.parseDouble(responseLixeiraC.getString(KEY_FIELD3)));

feedDetails4.setId('D');

feedDetails4.setPesoLixo(Double.parseDouble(responseLixeiraD.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

Calculatrice SmartBinService = new SmartBinService(); calculateur.montaListaLixeiras(feedList);

//Récupérer les composants

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

//Données réelles

Date currentTime = Calendar.getInstance().getTime(); SimpleDateFormat simpleDate = new SimpleDateFormat("jj/MM/aaaa"); String currentDate = simpleDate.format(currentTime); createDate.setText(KEY_DATE + currentDate + " "); listaDeLixeiras.setAdapter(adaptateur);

} catch (JSONException e) {

e.printStackTrace(); }

} autre {

Toast.makeText(MainActivity.this, "Une erreur s'est produite lors du chargement des données", Toast. LENGTH_LONG).show();

}

} }); }

Agora, na tela inicial do aplicativo, serão listados os dados de cada lixeira.

Étape 7: Mostando No Mapa

Mostando No Mapa
Mostando No Mapa

Ainda na atividade principal, vamos adicionar uma ação a ser relacionada ao botão Mapa, na tela inicial.

/** Appelé lorsque l'utilisateur appuie sur le bouton Mapa */ public void openMaps(View view) { Intent intent = new Intent(this, LixeiraMapsActivity.class);

//Passa a lista de lixeiras

Bundle bundle = new Bundle(); bundle.putParcelableArrayList("lixeiras", feedList); intention.putExtras(paquet);

startActivity(intention);

}

No mapa, temos três atividades a executar:

  1. marcar a posição atual do caminha de lixo
  2. marcar os pontos correspondantes a cada lixeira no mapa
  3. traçar a rota entre os pontos

Para executar os passos acima, vamos usar a API Google Directions. Para desenhar as rotas, foram seguidos os passos do tutorial Dessin d'itinéraires routiers entre deux emplacements à l'aide de Google Directions dans Google Map Android API V2

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

//Emplacements

courant LatLng privé;

privé LatLng lixeiraA; privé LatLng lixeiraB; privé LatLng lixeiraC; privé LatLng lixeiraD;.

Para adicionar a posição atual no mapa, foi criado o método:

private void checkLocationandAddToMap() { //Vérification si l'utilisateur a accordé l'autorisation if (ActivityCompat.checkSelfPermission(this, android. Manifest.permission. ACCESS_FINE_LOCATION) != PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android. Manifest.permission. ACCESS_COARSE_LOCATION) != PackageManager. PERMISSION_GRANTED) { //Demande de l'autorisation de localisation ActivityCompat.requestPermissions(this, new String{android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); revenir; }

//Récupération du dernier emplacement connu en utilisant le Fus

Emplacement de l'emplacement = LocationServices. FusedLocationApi.getLastLocation(googleApiClient);

// Les MarkerOptions sont utilisées pour créer un nouveau Marker. Vous pouvez spécifier l'emplacement, le titre, etc. avec MarkerOptions

this.current = new LatLng(location.getLatitude(), location.getLongitude()); MarkerOptions markerOptions = new MarkerOptions().position(current).title("Position atual");

//Ajout du marqueur créé sur la carte, déplacement de la caméra vers la position

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

// Déplacez la caméra instantanément vers l'emplacement avec un zoom de 15.

mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(current, 15));

// Zoom avant, animation de la caméra.

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

}

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

private void addBinALocation() { //Vérification si l'utilisateur a accordé l'autorisation if (ActivityCompat.checkSelfPermission(this, android. Manifest.permission. ACCESS_FINE_LOCATION) != PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android. Manifest.permission. ACCESS_COARSE_LOCATION) != PackageManager. PERMISSION_GRANTED) { //Demande de l'autorisation de localisation ActivityCompat.requestPermissions(this, new String{android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); revenir; }

//Praça da Estação

double latitude = -19,9159578; double longitude = -43,9387856; this.lixeiraA = new LatLng(latitude, longitude);

MarkerOptions markerOptions = new MarkerOptions().position(lixeiraA).title("Lixeira A");

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

Comme posições de latitude et longitude de cada lixeira foram recuperadas atraves do próprio Google Maps, e deixadas fixas no código. Idealmente, estes valores ficariam salvos em um banco de dados (par exemple Firebase). Será a primeira evolução deste projeto!

O último passo agora é traçar as rotas entre os pontos. Para tal, um conceito muito important, e que será utilizado neste projeto, são os Waypoints!

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

private String getDirectionsUrl (origine LatLng, destination LatLng, liste waypointsList){

// Origine du parcours

String str_origin = "origin="+origin.latitude+", "+origin.longitude;

// Destination de l'itinéraire

String str_dest = "destination="+dest.latitude+", "+dest.longitude;

//Points de cheminement le long de l'itinéraire

//waypoints=optimize:true|-19.9227365, -43.9473546|-19.9168006, -43.9361124 String waypoints = "waypoints=optimize:true"; pour (point LatLng: waypointsList){ waypoints += "|" + point.latitude + ", " + point.longitude; }

// Capteur activé

Capteur de chaîne = "sensor=false";

// Construire les paramètres du service Web

Paramètres de chaîne = str_origin+"&"+str_dest+"&"+sensor + "&" + waypoints;

// Format de sortie

Sortie de chaîne = "json";

// Construction de l'url du service web

URL de chaîne = "https://maps.googleapis.com/maps/api/directions/"+output+"?"+parameters; System.out.println("++++++++++++++++ "+url);

URL de retour;

}

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

@Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap;

vérifierEmplacementetAjouteràMap();

if (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(); }

//Tracer des itinéraires

// Obtenir l'URL de l'API Google Directions

Points de liste = new ArrayList(); points.add(lixeiraB); points.add(lixeiraC); points.add(lixeiraD);

URL de chaîne = getDirectionsUrl(current, lixeiraA, points);

DownloadTask downloadTask = new DownloadTask(); // Commencer à télécharger les données json à partir de l'API Google Directions downloadTask.execute(url); }

Aqui passamos apenas pelos pontos principais. O código completo do projeto será disponibilizado para consulta.

Étape 8: Conclusion

Este foi um projeto trabalhando conceitos de IoT, mostrando uma das varias opções de conectar dispositivos atraves da nuvem, efetuar tomada decisões sem interferência humana direta. Em anexo, segue um vídeo do projeto completeo, para ilustração, e os fontes das atividades criadas no Android.

Conseillé: