Índice:

SmartBin: 8 etapas
SmartBin: 8 etapas

Vídeo: SmartBin: 8 etapas

Vídeo: SmartBin: 8 etapas
Vídeo: How to Track Steps on XIAOMI Smart Band 8 2024, Novembro
Anonim
SmartBin
SmartBin

Este é um projeto para um sistema inteligente de coletas, no qual os caminhões de lixo fornecem dados das lixeiras, identificando a quantidade de lixo presente em cada uma delas, e uma rota de coleta traçada, com base nas informações recuperadas.

Para montar este projeto, é necessário:

  • NodeMCU
  • Sensor Ultrassônico de Distancia
  • Caixa de papelão
  • Protoboard
  • Cabos
  • Dispositivo Android

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

// define os números dos pinos # 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.

float cmMsec, inMsec;

microsec longo = ultra-som. sincronismo ();

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

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

// Exibe informações sem monitor serial

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

Serial.print (cmMsec);

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

Serial.println (inMsec);

Dados da string = String (cmMsec);

Serial.println (dados);

Etapa 2: Montando a Lixeira

Agora, vamos montar a lixeira inteligente. Precisaremos conectar o sensor ultrassônico no “teto” da lixeira. Por exemplo, utilizei um cabo e fita isolante. Em seguida, temos que avaliar 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 ultrassass, foi feito um algoritmo para salvar randomicamente a distancia lida em 4 lixeiras diferentes.

// Simulando 4 lixeiras

long lixeiraID;

void loop () {

lixeiraID = aleatório (1, 5);

}

Etapa 3: faça upload para um 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 fortes no site oficial.

De volta à aplicação, vamos usar uma 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 (previamente duas variáveis, ssid e pass , contendo o identificador e a senha de sua rede).

void connectWifi () {

Serial.print ("Conectando a" + * ssid);

WiFi.begin (SSID, aprovação);

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

atraso (500);

Serial.print (".");

}

Serial.println ("");

Serial.print ("Conectado na rede");

Serial.println (ssid);

Serial.print ("IP:");

Serial.println (WiFi.localIP ());

}

Durante a configuração, tentamos efetuar a conexão com a rede.

void setup () {

Serial.begin (9600);

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

// Conectando ao Wi-Fi

connectWifi ();

}

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.

void sendDataTS (float cmMsec, long id) {

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

Serial.println ("Enviando dados para o ThingSpeak");

String postStr = apiKey;

postStr + = "& field";

postStr + = id;

postStr + = "=";

postStr + = String (cmMsec);

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

Serial.println (postStr);

client.print ("POST / atualizar HTTP / 1.1 / n");

client.print ("Host: api.thingspeak.com / n");

client.print ("Conexão: fechar / n");

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

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

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

client.print (postStr.length ());

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

client.print (postStr);

atraso (1000);

}

client.stop ();

}

O primeiro parâmetro corresponde à distância em encontrada pelo sensor ultrassônico. O segundo parâmetro é o ID da lixeira que foi lida (que foi gerado aleatoriamente, 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.

Etapa 4: Recuperando Dados Do ThingSpeak

O ThingSpeak permite efetuar a leitura dos dados do seu canal, através de um serviço de retorno de um JSON. As diferentes opções para leitura do feed do seu canal estão 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 registrado anteriormente. Os mais importantes para 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 é uma URL que será lida do aplicativa Android, para recuperar os dados do ThingSpeak.

Etapa 5: Criando a Aplicação Android

Sem Android Studio, crie um novo projeto Android. Para o correto funcionamento da aplicação, é necessário configurar conforme abaixo no AndroidManifest.

Para usar o Google Maps, será necessário pegar uma chave junto ao Google. Siga os passos obtidos no link Obter chave de API.

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

A chave de API para APIs baseadas no Google Maps é definida como um recurso de string.

(Veja o arquivo "res / values / google_maps_api.xml").

Observe que a chave de API está vinculada à chave de criptografia usada para assinar o APK. Você precisa de uma chave de API diferente para cada chave de criptografia, incluindo a chave de liberação que é usada para assinar o APK para publicação. Você pode definir as chaves para os destinos de depuração e liberação em src / debug / e src / release /.

<meta-dados

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

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

A configuração completa está mo arquivo AndroidManifest anexado ao projeto.

n

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

private String url_a = "https://api.thingspeak.com/channels/429823/fields/1/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; private String url_b = "https://api.thingspeak.com/channels/429823/fields/2/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; private String url_c = "https://api.thingspeak.com/channels/429823/fields/3/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; private String 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 classe do Android específica, chamada JSONObject. Mais uma vez, vamos criar um objeto para cada URL:

JSONObject responseLixeiraA; JSONObject responseLixeiraB; JSONObject responseLixeiraC; JSONObject responseLixeiraD;

Para abrir uma conexão com como urls, vamos usar uma classe auxiliar, chamada HttpJsonParser. Esta classe será responsável por abrir uma conexão com um URL, efetuar leitura dos dados encontrados, e retornar o objeto JSON montado.

public JSONObject makeHttpRequest (String url, método String, Parâmetros do mapa) {

Experimente {

Construtor Uri. Builder = novo Uri. Builder (); URL urlObj; String encodedParams = ""; if (params! = null) {for (entrada Map. Entry: params.entrySet ()) {builder.appendQueryParameter (entry.getKey (), entry.getValue ()); }} if (builder.build (). getEncodedQuery ()! = null) {encodedParams = builder.build (). getEncodedQuery ();

}

if ("GET".equals (método)) {url = url + "?" + encodedParams; urlObj = novo URL (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod (método);

} outro {

urlObj = novo URL (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod (método); urlConnection.setRequestProperty ("Content-Type", "application / x-www-form-urlencoded"); urlConnection.setRequestProperty ("Content-Length", String.valueOf (encodedParams.getBytes (). length)); urlConnection.getOutputStream (). write (encodedParams.getBytes ()); } // Conecte-se ao servidor urlConnection.connect (); // Leia a resposta is = urlConnection.getInputStream (); Leitor BufferedReader = novo BufferedReader (novo InputStreamReader (é)); StringBuilder sb = new StringBuilder (); String line;

// Analisa a resposta

while ((line = reader.readLine ())! = null) {sb.append (line + "\ n"); } is.close (); json = sb.toString (); // Converta a resposta para 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", "Erro ao analisar dados" + e.toString ()); } catch (Exception e) {Log.e ("Exception", "Error parsing data" + e.toString ()); }

// retornar objeto JSON

return jObj;

}

}

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

@Override protected String doInBackground (String… params) {HttpJsonParser jsonParser = new HttpJsonParser ();

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

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

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

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

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

Exibir mainView = (Exibir) findViewById (R.id.activity_main); if (sucesso == 1) {try {// Cria feedDetail para cada lixeira Lixeira feedDetails1 = new Lixeira (); Lixeira feedDetails2 = nova Lixeira (); Lixeira feedDetails3 = nova Lixeira (); Lixeira feedDetails4 = nova 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

Calculadora SmartBinService = novo SmartBinService (); calculator.montaListaLixeiras (feedList);

// Recupera componentes

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

// Dados atuais

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

} catch (JSONException e) {

e.printStackTrace (); }

} outro {

Toast.makeText (MainActivity.this, "Ocorreu algum erro ao carregar dados", Toast. LENGTH_LONG).show ();

}

} }); }

Agora, na tela inicial do aplicativo, será necessário os dados de cada lixeira.

Etapa 7: Mostrando No Mapa

Mostrando No Mapa
Mostrando No Mapa

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

/ ** Chamado quando o usuário toca no botão Mapa * / public void openMaps (Exibir visualização) {Intent intent = new Intent (this, LixeiraMapsActivity.class);

// Passa a lista de lixeiras

Bundle bundle = new Bundle (); bundle.putParcelableArrayList ("lixeiras", feedList); intent.putExtras (pacote);

startActivity (intenção);

}

No mapa, temos três atividades a executar:

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

Para executar os passos acima, vamos usar uma API do Google Directions. Para desenhar rotas, foram seguidos os passos do tutorial Desenhar direções de rota de direção entre dois locais usando o Google Directions no Google Map Android API V2

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

//Localizações

LatLng privado atual;

LatLng lixeiraA privada; LatLng lixeiraB privado; LatLng lixeiraC privado; private LatLng lixeiraD;.

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

private void checkLocationandAddToMap () {// Verificando se o usuário concedeu a permissão 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) {// Solicitando a permissão de localização ActivityCompat.requestPermissions (this, new String {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); Retorna; }

// Buscando o último local conhecido usando o Fus

Location location = LocationServices. FusedLocationApi.getLastLocation (googleApiClient);

// MarkerOptions são usados para criar um novo Marker. Você pode especificar a localização, título, etc. com MarkerOptions

this.current = novo LatLng (location.getLatitude (), location.getLongitude ()); MarkerOptions markerOptions = new MarkerOptions (). Position (current).title ("Posição atual");

// Adicionando o marcador criado no mapa, movendo a câmera para a posição

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

// Mova a câmera instantaneamente para o local com um zoom de 15.

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

// Amplie, animando a câmera.

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

}

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

private void addBinALocation () {// Verificando se o usuário concedeu a permissão 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) {// Solicitando a permissão de localização ActivityCompat.requestPermissions (this, new String {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); Retorna; }

// Praça da Estação

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

MarkerOptions markerOptions = novo MarkerOptions (). Position (lixeiraA).title ("Lixeira A");

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

As posições de latitude e longitude 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 (por exemplo Firebase). Será a primeira evolução deste projeto!

O último passo agora é traçar como 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 uma rota entre dois pontos de dados:

private String getDirectionsUrl (LatLng origin, LatLng dest, List waypointsList) {

// Origem da rota

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

// Destino da rota

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

// Waypoints ao longo da rota

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

// Sensor habilitado

Sensor de string = "sensor = false";

// Construindo os parâmetros para o serviço da web

Parâmetros de string = str_origin + "&" + str_dest + "&" + sensor + "&" + waypoints;

// Formato de saída

Saída de string = "json";

// Construindo o url para o serviço da web

String url = "https://maps.googleapis.com/maps/api/directions/"+output+"?"+parameters; System.out.println ("++++++++++++++" + url);

return url;

}

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

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

checkLocationandAddToMap ();

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

// Desenhe rotas

// Obtendo URL para a API de rotas do Google

Pontos da lista = novo ArrayList (); points.add (lixeiraB); points.add (lixeiraC); points.add (lixeiraD);

String url = getDirectionsUrl (atual, lixeiraA, pontos);

DownloadTask downloadTask = novo DownloadTask (); // Comece a baixar dados json da API de rotas do Google downloadTask.execute (url); }

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

Etapa 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ão sem interferência humana direta. Em anexo, segue um vídeo do projeto completo, para ilustração, e os fontes das atividades no Android.

Recomendado: