Tabla de contenido:

SmartBin: 8 pasos
SmartBin: 8 pasos

Video: SmartBin: 8 pasos

Video: SmartBin: 8 pasos
Video: Mi BAND 8: 10 TRUCOS INCREÍBLES | Tips & Tricks en Español 2024, Noviembre
Anonim
SmartBin
SmartBin

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

Para montar este proyecto, é necesario:

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

Paso 1: Conectando O Sensor

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

// define los números de los pines #define pino_trigger 2 // D4

#define pino_echo 0 // D3

Para efetuar una lectura de dos datos del sensor, seguido o tutorial elaborado pelo FilipeFlop, disponível aqui.

float cmMsec, inMsec;

microsec largo = tiempo ultrasónico ();

cmMsec = ultrasónico.convert (microsec, Ultrasónico:: CM);

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

// Exibe informacoes sin monitor serial

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

Serial.print (cmMsec);

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

Serial.println (inMsec);

Datos de cadena = Cadena (cmMsec);

Serial.println (datos);

Paso 2: Montando a Lixeira

Agora, vamos montar a lixeira inteligente. Precisaremos conectar o sensor ultrassônico no "teto" da lixeira. Para o ejemplo, utilizei 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 distancia lida em 4 lixeiras diferentes.

// Simulando 4 lixeiras

long lixeiraID;

bucle vacío () {

lixeiraID = aleatorio (1, 5);

}

Paso 3: Subir Para a Nuvem

Agora, precisamos enviar estes dados para a nuvem. Eu escolhi o ThingSpeak, por familiaridade com o mesmo. Primeiramente, es necesario criar um novo canal, recebendo 4 parâmetros, referencias ao volumen de cada lixeira.

Para conectar una aplicación con ThingSpeak, es necesario salvar el número de API do canal criado. Siga los pasos descritos en el sitio oficial.

De volta à aplicação, vamos a utilizar una 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 , contendo o identificador e a senha de sua rede).

void connectWifi () {

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

WiFi.begin (ssid, pase);

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

retraso (500);

Serial.print (".");

}

Serial.println ("");

Serial.print ("Conectado na rede");

Serial.println (ssid);

Serial.print ("IP:");

Serial.println (WiFi.localIP ());

}

Durante la instalación, tentamos efetuar a conexão com a rede.

configuración vacía () {

Serial.begin (9600);

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

// Conectando ao Wi-Fi

connectWifi ();

}

E, para enviar os dados para o ThingSpeak, basta abrir una conexión HTTP padrão, pasar o número de API e os parâmetros.

void sendDataTS (float cmMsec, identificación larga) {

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

Serial.println ("Enviando datos para ThingSpeak");

String postStr = apiKey;

postStr + = "& campo";

postStr + = id;

postStr + = "=";

postStr + = Cadena (cmMsec);

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

Serial.println (postStr);

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

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

client.print ("Conexión: cerrar / n");

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

client.print ("Tipo de contenido: aplicación / x-www-form-urlencoded / n");

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

client.print (postStr.length ());

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

client.print (postStr);

retraso (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 también sirve para identificar para cual campo será feito o upload do valor lido.

Paso 4: Recuperando Dados Do ThingSpeak

O ThingSpeak permite efetuar leitura dos dados do seu canal, através de um serviço retornando um JSON. Como 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 leer directamente los datos 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 sin enlace previamente informado. 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 es una URL que será lida para la aplicativa Android, para recuperar los datos de ThingSpeak.

Paso 5: Criando una aplicación Android

Sin Android Studio, crie um novo projeto Android. Para el correcto funcionamiento de la aplicación, es necesario configurar como permisos abaixo sin AndroidManifest.

Para utilizar Google Maps, será necesario pegar uma chave junto a Google. Siga los pasos descritos sin enlace Obter chave de API.

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

La clave de API para las API basadas en Google Maps se define como un recurso de cadena.

(Consulte el archivo "res / values / google_maps_api.xml").

Tenga en cuenta que la clave de API está vinculada a la clave de cifrado utilizada para firmar el APK. Necesita una clave de API diferente para cada clave de cifrado, incluida la clave de versión que se utiliza para firmar el APK para su publicación. Puede definir las claves para los destinos de depuración y liberación en src / debug / y src / release /.

<metadatos

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

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

Una configuración completa está más archivada AndroidManifest anexado ao proyecto.

norte

Paso 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 una lectura de dos datos, iremos a utilizar una clase específica de Android, chamada JSONObject. Mais uma vez, vamos a criar um objeto para cada URL:

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

Para abrir una conexión com como urls, vamos a usar criar uma classe auxiliar, chamada HttpJsonParser. Esta clase será una respuesta para abrir una conexión con la URL, efetuar la lectura de los datos encontrados, y devolver el objeto JSON montado.

public JSONObject makeHttpRequest (String url, String método, Map params) {

tratar {

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

}

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

} demás {

urlObj = nueva URL (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod (método); urlConnection.setRequestProperty ("Tipo de contenido", "aplicación / x-www-form-urlencoded"); urlConnection.setRequestProperty ("Content-Length", String.valueOf (encodedParams.getBytes (). length)); urlConnection.getOutputStream (). write (encodedParams.getBytes ()); } // Conectarse al servidor urlConnection.connect (); // Leer la respuesta es = urlConnection.getInputStream (); Lector BufferedReader = nuevo BufferedReader (nuevo InputStreamReader (is)); StringBuilder sb = nuevo StringBuilder (); Línea de cuerda;

// Analizar la respuesta

while ((línea = lector.readLine ())! = nulo) {sb.append (línea + "\ n"); } está cerca(); json = sb.toString (); // Convierta la respuesta a JSON Object jObj = new JSONObject (json);

} captura (UnsupportedEncodingException e) {

e.printStackTrace (); } captura (ProtocolException e) {e.printStackTrace (); } captura (IOException e) {e.printStackTrace (); } catch (JSONException e) {Log.e ("Analizador JSON", "Error al analizar datos" + e.toString ()); } catch (Exception e) {Log.e ("Exception", "Error al analizar datos" + e.toString ()); }

// devuelve el objeto JSON

return jObj;

}

}

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

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

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

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

devolver nulo;}

Quando o método doInBackgroundé encerrado, o control de ejecução do Android pasa para o método onPostExecute. Neste método, vamos a criar los objetos Lixeira, e popular com os dados recuperados do ThingSpeak:

onPostExecute vacío protegido (resultado de cadena) {pDialog.dismiss (); runOnUiThread (new Runnable () {public void run () {

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

Ver mainView = (Ver) findViewById (R.id.activity_main); if (éxito == 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

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

// Recupera componentes

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

// Datos atual

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

} captura (JSONException e) {

e.printStackTrace (); }

} demás {

Toast.makeText (MainActivity.this, "Se produjo un error al cargar datos", Toast. LENGTH_LONG).show ();

}

} }); }

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

Paso 7: Mostrar sin mapa

Mostrando sin mapa
Mostrando sin mapa

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

/ ** Se llama cuando el usuario presiona el botón Mapa * / public void openMaps (Vista de vista) {Intent intent = new Intent (esto, LixeiraMapsActivity.class);

// Passa a lista de lixeiras

Bundle bundle = nuevo Bundle (); bundle.putParcelableArrayList ("lixeiras", feedList); intent.putExtras (paquete);

startActivity (intención);

}

Sin mapa, temos três atividades a ejecutar:

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

Para ejecutar los pasos acima, vamos a usar una API de Google Directions. Para desenhar as rotas, foram seguidos os passos do tutorial Dibujar direcciones de ruta de conducción entre dos ubicaciones usando Google Directions en Google Map Android API V2

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

// Ubicaciones

corriente LatLng privada;

privado LatLng lixeiraA; privado LatLng lixeiraB; LatLng lixeiraC privado; Privado LatLng lixeiraD;.

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

private void checkLocationandAddToMap () {// Verificando si el usuario ha otorgado el permiso si (ActivityCompat.checkSelfPermission (this, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (this, android. Manifest. ACCESS_COARSE_LOCATION)! = PackageManager. PERMISSION_GRANTED) {// Solicitando el permiso de ubicación ActivityCompat.requestPermissions (esto, nueva Cadena {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); regreso; }

// Obteniendo la última ubicación conocida usando el Fus

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

// MarkerOptions se utiliza para crear un nuevo Marker. Puede especificar la ubicación, título, etc. con MarkerOptions

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

// Añadiendo el marcador creado en el mapa, moviendo la cámara a la posición

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

// Mueva la cámara instantáneamente a la ubicación con un zoom de 15.

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

// Acercar, animando la cámara.

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

}

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

private void addBinALocation () {// Verificando si el usuario ha otorgado el permiso si (ActivityCompat.checkSelfPermission (this, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (esto, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (esto, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (esto, android. Manifest.permission. ACCESS_COARSE_LOCATION)! = PackageManager. PERMISSION_GRANTED) {// Solicitando el permiso de ubicación ActivityCompat.requestPermissions (esto, nueva Cadena {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); regreso; }

// Praça da Estação

latitud doble = -19,9159578; longitud doble = -43,9387856; this.lixeiraA = new LatLng (latitud, longitud);

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

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

Como posições de latitud y longitud de cada lixeira para recuperadas através del próprio Google Maps, e deixadas fijas sin código. Idealmente, estes valores ficariam salvos em um banco de datos (por ejemplo Firebase). ¡Será una primeira evolução deste projeto!

O último passo agora é traçar as rotas entre os pontos. ¡Para tal, um conceito muito importante, e que será utilizado en este proyecto, en los Waypoints!

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

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

// Origen de la ruta

String str_origin = "origen =" + origen.latitude + "," + origen.longitud;

// Destino de la ruta

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

// Waypoints a lo largo de la ruta

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

// Sensor habilitado

String sensor = "sensor = false";

// Construyendo los parámetros para el servicio web

Parámetros de cadena = str_origin + "&" + str_dest + "&" + sensor + "&" + waypoints;

// Formato de salida

Salida de cadena = "json";

// Construyendo la URL del servicio 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_TAMAÑO_GARBAGE) {addBinBLocation (); } if (lixeirasList.get (2).getVolumeLixo ()> Lixeira. MIN_VOLUME_GARBAGE || lixeirasList.get (2).getPesoLixo ()> Lixeira. MIN_TAMAÑO_GARBAGE) {addBinCLocation (); } if (lixeirasList.get (3).getVolumeLixo ()> Lixeira. MIN_VOLUME_GARBAGE || lixeirasList.get (3).getPesoLixo ()> Lixeira. MIN_TAMAÑO_GARBAGE) {addBinDLocation (); }

// Dibujar rutas

// Obtener la URL de la API de Google Directions

Puntos de lista = new ArrayList (); puntos.add (lixeiraB); puntos.add (lixeiraC); puntos.add (lixeiraD);

String url = getDirectionsUrl (actual, lixeiraA, puntos);

DownloadTask downloadTask = new DownloadTask (); // Comience a descargar datos json de la API de Google Directions downloadTask.execute (url); }

Aqui pasamos apenas pelos pontos principais. O código completo do projeto estará disponible para consulta.

Paso 8: Conclusión

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

Recomendado: