Taula de continguts:

SmartBin: 8 passos
SmartBin: 8 passos

Vídeo: SmartBin: 8 passos

Vídeo: SmartBin: 8 passos
Vídeo: Como Configurar a XIAOMI MI BAND 8 | Passo a Passo 2024, De novembre
Anonim
SmartBin
SmartBin

Aquest és un projecte per a un sistema intel·ligent de coletes, no qual os caminhões de lixo recebem dados das lixeiras, identificant a quantitat de lixo present em cada uma delas, e uma rota de coleta traçada, com base nas informações recuperades.

Per muntar aquest projecte, és necessari:

  • NodeMCU
  • Sensor Ultrassônico de Distància
  • Caixa de paper
  • Protoboard
  • Cabos
  • Dispositiu Android

Pas 1: Conectant el sensor O

Primeiramente, vamos efetuar a conexão entre o sensor ultrassônico e o NODEMCU. Per tant, podem connectar-nos com portes trigger e echo do sensor nas portas D4 e D3 do NodeMCU:

// defineix els números de pins #define pino_trigger 2 // D4

#define pino_echo 0 // D3

Per efetuar a leitura dos dados do sensor, va ser seguit o tutorial elaborat pelo FilipeFlop, disposível aqui.

float cmMsec, inMsec;

long microsec = ultrasonic.timing ();

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

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

// Exibe informacoes no monitor serial

Serial.print ("Distància em cm:");

Serial.print (cmMsec);

Serial.print ("- Distància em polegades:");

Serial.println (inMsec);

Dades de cadena = Cadena (cmMsec);

Serial.println (dades);

Pas 2: Montant a Lixeira

Agora, vamos montar a lixeira inteligente. Precisarem connectar o sensor ultrassônico no “teto” da lixeira. Per exemple, utilitzeu un cable i una fita aïllants. Em seguida, tenim que medir a distància inicial, per saber o valor per a una lixeira vazia. No meu caso, foi de 26, 3cm. Esse é o valor que considerarmos per uma lixeira vazia.

Per simulação, vist que no tinc més de un sensor ultrassònic, es va fer amb un algoritme per salvar aleatòriament a distància lida en 4 lixes diferents.

// Simulant 4 lixeiras

llarg lixeiraID;

bucle buit () {

lixeiraID = aleatori (1, 5);

}

Pas 3: pengeu Para a Nuvem

Agora, precisamos enviar estes dados para a nuvem. Eu escolhi o ThingSpeak, per familiaritat com fins i tot. Primerament, és necessari tenir un canal nou, rebent 4 paràmetres, referents al volum de cada lixeira.

Connecteu-vos a l'aplicació amb ThingSpeak, és necessari salvar el número de l'API del canal criat. Siga os passos descritos no site oficial.

De volta a aplicação, vamos use a biblioteca ESP8266WiFi.h for efetuar connection with ThingSpeak, and transferir os dados.

Primeiramente, uma função para efetuar conexão com a rede (definició prèviament de dues varietats, ssid e pass , contenint o identificador i a senha de la seva rede).

void connectWifi () {

Serial.print ("Connexió a" + * 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 ());

}

Durant la configuració, intentem establir una connexió amb reduir.

configuració nul·la () {

Serial.begin (9600);

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

// Conectant-se al Wi-Fi

connectWifi ();

}

I, per enviar-los per a ThingSpeak, basta d'obrir una connexió HTTP de paquet, passant el número de l'API i els paràmetres.

void sendDataTS (float cmMsec, id llarg) {

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

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

Cadena postStr = apiKey;

postStr + = "& camp";

postStr + = id;

postStr + = "=";

postStr + = String (cmMsec);

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

Serial.println (postStr);

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

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

client.print ("Connexió: tancar / n");

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

client.print ("Tipus de contingut: application / x-www-form-urlencoded / n");

client.print ("Longitud del contingut:");

client.print (postStr.length ());

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

client.print (postStr);

retard (1000);

}

client.stop ();

}

El primer paràmetre correspon a distàncies amb centímetres trobats amb un sensor ultrasonic. O segundo parámetro é o ID da lixeira que foi lida (que va ser gerat aleatòriament, amb un número de 1 a 4).

O ID da lixeira serve também per identificar per qual camp serà fet o upload do valor lido.

Pas 4: Recuperant Dados Do ThingSpeak

O ThingSpeak permet efetuar leitura dos dados do seu canal, através de um servei retornant um JSON. As different opções for leitura do feed do their 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 patró d'URL per a aquest cenari és:

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

Cada camp està descrit sense enllaç informat prèviament. Els més importants per al seu projecte:

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

Aquesta és una URL que serà aplicable a Android, per recuperar-los amb ThingSpeak.

Pas 5: Criant a l'aplicació d'Android

No hi ha Android Studio, amb un nou projecte Android. Per al funcionament correcte de l'aplicació, és necessari configurar-ho com a permís per AndroidManifest.

Per utilitzar Google Maps, serà necessari incloure-ho juntament amb 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 clau API per a les API basades en Google Maps es defineix com un recurs de cadena.

(Vegeu el fitxer "res / values / google_maps_api.xml").

Tingueu en compte que la clau API està vinculada a la clau de xifratge que s’utilitza per signar l’APK. Necessiteu una clau d'API diferent per a cada clau de xifratge, inclosa la clau de llançament que s'utilitza per signar l'APK per publicar-la. Podeu definir les claus dels objectius de depuració i alliberament a src / debug / i src / release /.

<metadades

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

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

Una configuració completa està ara arxiu AndroidManifest anexat al projecte.

n

Pas 6: Recuperant O Feed sense Android

A principal activitat no Android, MainActivity, crie 4 variáveis per identificar cada um dos canais do ThingSpeak a sered 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";

Per efetuar a leitura dos dados, utilitzarem una classe de Android específica, chamada JSONObject. Més uma vegada, vamos criar amb un objecte per a cada URL:

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

Per obrir una connexió com a URL, vamos usar criar uma classe auxiliar, chamada HttpJsonParser. Aquesta classe serà responsable per obrir una connexió amb un URL, efetuar la lectura dos dies trobats, i retornar o objecte JSON muntat.

public JSONObject makeHttpRequest (URL de cadena, mètode de cadena, paràmetres del mapa) {

provar {

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

}

if ("GET".equals (mètode)) {url = url + "?" + codificatParams; urlObj = URL nova (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod (mètode);

} més {

urlObj = URL nova (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod (mètode); urlConnection.setRequestProperty ("Content-Type", "application / x-www-form-urlencoded"); urlConnection.setRequestProperty ("Content-Length", String.valueOf (encodedParams.getBytes (). length)); urlConnection.getOutputStream (). write (encodedParams.getBytes ()); } // Connecteu-vos al servidor urlConnection.connect (); // Llegiu la resposta is = urlConnection.getInputStream (); Lector BufferedReader = nou BufferedReader (nou InputStreamReader (és)); StringBuilder sb = new StringBuilder (); Línia de corda;

// Analitzeu la resposta

while ((line = reader.readLine ())! = null) {sb.append (line + "\ n"); } és.close (); json = sb.toString (); // Converteix la resposta en objecte JSON jObj = new JSONObject (json);

} catch (UnsupportedEncodingException e) {

e.printStackTrace (); } catch (ProtocolException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } catch (JSONException e) {Log.e ("Analitzador JSON", "Error en analitzar dades" + e.toString ()); } catch (Exception e) {Log.e ("Exception", "Error en analitzar dades" + e.toString ()); }

// torna l'objecte JSON

tornar jObj;

}

}

De volta a atividade principal, vamos efetuar a chamada às urls de forma assíncrona, escrivint aquest codi dins del mètode doInBackground.

@Override String DoInBackground protegit (String … params) {HttpJsonParser jsonParser = new HttpJsonParser ();

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

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

retorn nul;}

Quan o mètode doInBackgroundé encerrat, o control d’execució de Android passa per al mètode onPostExecute. En aquest mètode, podem criar els objectes de Lixeira, i populars amb els nostres recuperats de ThingSpeak:

void protegit onPostExecute (resultat de la cadena) {pDialog.dismiss (); runOnUiThread (nou Runnable () {public void run () {

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

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

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 = SmartBinService nou (); calculator.montaListaLixeiras (feedList);

// Recupera components

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

// Dades atuals

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

} més {

Toast.makeText (MainActivity.this, "S'ha produït un error en carregar les dades", Toast. LENGTH_LONG).show ();

}

} }); }

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

Pas 7: Mostrando No Mapa

Mostrando No Mapa
Mostrando No Mapa

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

/ ** Es truca quan l'usuari toca el botó Mapa * / public void openMaps (Visualització de la vista) {Intent intent = new Intent (this, LixeiraMapsActivity.class);

// Passa a llista de lixeiras

Paquet de paquets = paquet nou (); bundle.putParcelableArrayList ("lixeiras", feedList); intent.putExtras (paquet);

startActivity (intenció);

}

No hi ha mapa, tenim activitats a executar:

  1. marcar a posição atual do caminha de lixo
  2. marcar els ponts corresponents a cada lixeira no mapa
  3. traçar a rota entre els ponts

Per executar els passos màxims, usem una API de Google Directions. Per desenvolupar com a rotacions, seguint els passos del tutorial.

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

// Ubicacions

corrent LatLng privat;

private LatLng lixeiraA; private LatLng lixeiraB; private LatLng lixeiraC; privat LatLng lixeiraD;.

Per afegir una posició actual al mapa, es va crear o mètode:

private void checkLocationandAddToMap () {// Comprovar si l'usuari ha concedit el permís si (ActivityCompat.checkSelfPermission (this, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (this, android. Manifest). ACCESS_COARSE_LOCATION)! = PackageManager. PERMISSION_GRANTED) {// Sol·licitud del permís de ubicació ActivityCompat.requestPermissions (aquesta, nova cadena {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); tornar; }

// Recuperació de l'última ubicació coneguda mitjançant el Fus

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

// MarkerOptions s’utilitza per crear un marcador nou. Podeu especificar la ubicació, el títol, etc. amb MarkerOptions

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

// Afegint el marcador creat al mapa, movent la càmera a la seva posició

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

// Mou la càmera instantàniament a la ubicació amb un zoom de 15.

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

// Amplieu, animant la càmera.

mMap.animateCamera (CameraUpdateFactory.zoomTo (14), 2000, nul·la);

}

Em seguida, per a cada lixeira, foram criats mètodes similars a l'abaixo:

private void addBinALocation () {// Comprovar si l'usuari ha concedit el permís si (ActivityCompat.checkSelfPermission (this, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (this, android. Manifest.permission. ACCESS_COARSE_LOCATION)! = PackageManager. PERMISSION_GRANTED) {// Sol·licitud del permís de ubicació ActivityCompat.requestPermissions (aquesta, nova cadena {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); tornar; }

// Praça da Estação

doble latitud = -19,9159578; doble longitud = -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); }

As posições de latitude e longitude de cada lixeira foram recuperades através do próprio Google Maps, e deixadas fixas no code. Idealment, aquests valors fitxers els salvem amb un banc de dats (per exemple, Firebase). Serà la primera evolució d'aquest projecte!

O último passo agora é traçar as rotas entre os pontos. Per tal, amb un concepte molt important, i que s'utilitzarà en aquest projecte, fins a Waypoints!

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

private String getDirectionsUrl (origen LatLng, dest. LatLng, Llista waypoints Llista) {

// Origen de la ruta

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

// Destinació de la ruta

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

// Punts de ruta al llarg de la ruta

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

// Sensor habilitat

Sensor de cadena = "sensor = fals";

// Construir els paràmetres del servei web

Paràmetres de cadena = str_origin + "&" + str_dest + "&" + sensor + "&" + waypoints;

// Format de sortida

Cadena de sortida = "json";

// Creació de l'URL del servei web

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

URL de retorn;

}

I, per fim, juntant tot el mètode principal de la 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 (); }

// Dibuixa rutes

// Obtenir URL a l'API Google Directions

Llista de punts = nova llista Array (); points.add (lixeiraB); points.add (lixeiraC); points.add (lixeiraD);

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

DownloadTask downloadTask = nova DownloadTask (); // Comenceu a baixar dades JSON des de Google Directions API downloadTask.execute (url); }

Aqui passem apenas pelos pontos principals. El codi complet del projecte estarà disponible per a la consulta.

Pas 8: Conclusió

Este foi um projecte treballant conceptes de IoT, mostrant uma das várias opcions de connectar dispositius atravessats de nuvem, e efetuar tomada de decisionsões sem interferência humana direta. Anexo, segueix un vídeo completament projectat, per a il·lustració, i les fonts de les activitats criades no Android.

Recomanat: