Sadržaj:

SmartBin: 8 koraka
SmartBin: 8 koraka

Video: SmartBin: 8 koraka

Video: SmartBin: 8 koraka
Video: Автомобильный генератор BMW 12 В 180 А к генератору с помощью зарядного устройства для ноутбука 2024, Studeni
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 nas informações recuperadas.

Para montar este projeto, é needário:

  • NodeMCU
  • Senzor Ultrassônico de Distancia
  • Caixa de papelão
  • Protoboard
  • Cabos
  • Dispozitivo Android

Korak 1: Conectando O osjetnik

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

// definira brojeve pinova #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;

dugačak mikrosek = ultrazvučni.timing ();

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

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

// Exibe informacoes no serijski monitor

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

Serijski.ispis (cmMsec);

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

Serial.println (inMsec);

Podaci o nizu = String (cmMsec);

Serial.println (podaci);

Korak 2: Montando a Lixeira

Agora, vamos montar a lixeira inteligente. Precisaremos konektar ili senzor ultrassônico bez "teto" da lixeira. Para o exemplo, utilizei um cabo e fita isolante. Em seguida, temos que medir a distância inicial, para sabre o valor para a lixeira vazia. No meu caso, foi de 26, 3 cm. Esse é o valor que obzirrarmos para uma lixeira vazia.

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

// Simulando 4 lixeiras

dugi lixeiraID;

void loop () {{100} {101}

lixeiraID = slučajni (1, 5);

}

Korak 3: Prenesite Para a Nuvem

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

Pará conectar a applicação com o ThingSpeak, é needário salvar o número da API do kanala criado. Siga os passos descritos no site oficial.

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

void connectWifi () {

Serial.print ("Spajanje na"+ *ssid);

WiFi.početi (ssid, proći);

while (WiFi.status ()! = WL_CONNECTED) {{100} {101}

kašnjenje (500);

Serial.print (".");

}

Serial.println ("");

Serial.print ("Conectado na rede");

Serijski.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 …");

// Conectando ao Wi-Fi

connectWifi ();

}

E, para enviar os dados para 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 (poslužitelj, 80)) {

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

String postStr = apiKey;

postStr += "& polje";

postStr += id;

postStr += "=";

postStr += String (cmMsec);

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

Serial.println (postStr);

client.print ("POST /ažuriraj HTTP /1.1 / n");

client.print ("Domaćin: api.thingspeak.com / n");

client.print ("Veza: zatvori / 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);

kašnjenje (1000);

}

client.stop ();

}

O primeiro parâmetro korespondnde à distância em centímetros encontrada pelo senzor 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 služi também para identificar para qual campo será feito o upload do valor lido.

Korak 4: Recuperando Dados Do ThingSpeak

O ThingSpeak permite efetuar leitura dos dados do seu channel, através de um serviço retornando um JSON. Kako se 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 nema veze informado previamente. Os mais importantes para o projeto são:

  • CHANNEL_ID: número do seu kanal
  • FIELD_NUMBER: o número do campo
  • API_KEY: Chave de API do seu kanal

To je URL koji je dostupan za aplikaciju Android, za potrebe oporavka za ThingSpeak.

Korak 5: Criando i Aplicação Android

Nema Android Studija, pročitajte novi projekt za Android. Da biste primijenili funkcionalnost da biste primijenili, potrebno je konfigurirati kao dopuštenja za korištenje bez AndroidManifesta.

Za korištenje Google karata, poslužite se pehar 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.

API ključ za API-je zasnovane na Google kartama definiran je kao niz izvor.

(Pogledajte datoteku "res/values/google_maps_api.xml").

Imajte na umu da je API ključ povezan s ključem za šifriranje koji se koristi za potpisivanje APK -a. Za svaki ključ za šifriranje potreban vam je drugačiji API ključ, uključujući ključ za izdanje koji se koristi za potpisivanje APK -a za objavljivanje. Ključeve za ciljeve za ispravljanje pogrešaka i izdanje možete definirati u src/debug/i src/release/.

<metapodaci

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

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

Konfiguracija je potpuna u arkivu za AndroidManifest anexado ao projeto.

n

Korak 6: Recuperando O Feed Nema Androida

Na glavnoj aktivnoj adresi Androida, MainActivity, crije 4 varijante za identifikaciju kad umas kanalizirate 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"; privatni niz url_b = "https://api.thingspeak.com/channels/429823/fields/2/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; privatni niz 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";

Za efektivnu i pouzdanu upotrebu, iremos korištenja uma klase za Android específica, chamada JSONObject. Više o tome, vaš je cilj um objekat para cada URL:

JSONObjektni odgovorLixeiraA; JSONObjektni odgovorLixeiraB; JSONObjektni odgovorLixeiraC; JSONObject responseLixeiraD;

Para abrir a conexão com as 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, e retornar o objeto JSON montado.

javni JSONObject makeHttpRequest (URL niza, metoda niza, parametri karte) {

probaj {

Graditelj Uri. Builder = novi 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 (metoda)) {url = url + "?" + encodedParams; urlObj = novi URL (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod (metoda);

} else {

urlObj = novi URL (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod (metoda); urlConnection.setRequestProperty ("Content-Type", "application/x-www-form-urlencoded"); urlConnection.setRequestProperty ("Content-Length", String.valueOf (encodedParams.getBytes (). length)); urlConnection.getOutputStream (). write (encodedParams.getBytes ()); } // Povezivanje sa poslužiteljem urlConnection.connect (); // Pročitajte odgovor je = urlConnection.getInputStream (); Čitač BufferedReader = novi BufferedReader (novi InputStreamReader (je)); StringBuilder sb = novi StringBuilder (); Niz žica;

// Raščlanjivanje odgovora

while ((line = reader.readLine ())! = null) {sb.append (line + "\ n"); } je blizu(); json = sb.toString (); // Pretvorimo odgovor u JSON Objekt jObj = novi JSONObject (json);

} catch (UnsupportedEncodingException e) {

e.printStackTrace (); } catch (ProtocolException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } catch (JSONException e) {Log.e ("JSON Parser", "Greška pri raščlanjivanju podataka" + e.toString ()); } catch (Iznimka e) {Log.e ("Izuzetak", "Pogreška pri raščlanjivanju podataka" + e.toString ()); }

// vraća JSON objekt

povratak jObj;

}

}

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

@Override zaštićeni niz doInBackground (String… params) {HttpJsonParser jsonParser = novi HttpJsonParser ();

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

return null;}

Možete pristupiti doInBackgroundé koncertu ili kontrolirati izvršenje do Android prolaza za metodi naPostExecute. Neste método, vamos criar os objetos Lixeira, and popular com os dados recuperados do ThingSpeak:

zaštićena praznina naPostExecute (rezultat niza) {pDialog.dismiss (); runOnUiThread (new Runnable () {public void run () {

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

View mainView = (View) findViewById (R.id.activity_main); if (uspjeh == 1) {try {// Cria feedDetail para cada lixeira Lixeira feedDetails1 = nova 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

Kalkulator SmartBinService = novi SmartBinService (); kalkulator.montaListaLixeiras (feedList);

// Recupera komponente

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

// Podaci stvarni

Datum currentTime = Calendar.getInstance (). GetTime (); SimpleDateFormat simpleDate = novi SimpleDateFormat ("dd/MM/ggggg"); Niz currentDate = simpleDate.format (currentTime); createDate.setText (KEY_DATE + currentDate + ""); listaDeLixeiras.setAdapter (adapter);

} catch (JSONException e) {

e.printStackTrace (); }

} else {

Toast.makeText (MainActivity.this, "Došlo je do neke pogreške pri učitavanju podataka", Toast. LENGTH_LONG).show ();

}

} }); }

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

Korak 7: Mostrando bez karte

Mostrando Nema karte
Mostrando Nema karte

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

/ ** Poziva se kada korisnik dodirne gumb Mapa*/ public void openMaps (prikaz View) {Intent intent = new Intent (this, LixeiraMapsActivity.class);

// Passa a lista de lixeiras

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

startActivity (namjera);

}

Bez karte, temos três atividades i izvršitelj:

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

Kako biste izvršili aktivaciju, koristite API Google upute. Para desenhar as rotas, foram seguidos os passos do tutorial Crtanje uputa za rutu vožnje između dvije lokacije pomoću Google smjerova u Google Map Android API V2

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

// Lokacije

privatna LatLng struja;

privatni LatLng lixeiraA; privatni LatLng lixeiraB; privatni LatLng lixeiraC; privatni LatLng lixeiraD;.

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

private void checkLocationandAddToMap () {// Provjerava je li korisnik dodijelio dopuštenje if (ActivityCompat.checkSelfPermission (this, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkShell, ACCESS_COARSE_LOCATION)! = PackageManager. PERMISSION_GRANTED) {// Traženje dozvole za lokaciju ActivityCompat.requestPermissions (ovaj, novi niz {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); povratak; }

// Dohvaćanje posljednjeg poznatog mjesta pomoću Fusa

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

// MarkerOptions se koriste za stvaranje novog Marker -a. Možete odrediti lokaciju, naslov itd. Pomoću MarkerOptions

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

// Dodavanje stvorenog markera na kartu, pomicanje kamere u položaj

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

// Odmah premjestite kameru na lokaciju sa zumiranjem od 15.

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

// Povećajte prikaz, animirajući kameru.

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

}

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

private void addBinALocation () {// Provjerava je li korisnik odobrio dozvolu if (ActivityCompat.checkSelfPermission (this, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission this, ACCESS_COARSE_LOCATION)! = PackageManager. PERMISSION_GRANTED) {// Traženje dozvole za lokaciju ActivityCompat.requestPermissions (ovaj, novi niz {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); povratak; }

// Praça da Estação

dvostruka zemljopisna širina = -19,9159578; dvostruka zemljopisna dužina = -43.9387856; this.lixeiraA = novi LatLng (zemljopisna širina, dužina);

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

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

S obzirom na zemljopisnu širinu i dužinu na cada lixeira za recuperadas através do próprio Google Maps, e deixadas fixas no código. Idealno, 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 as 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:

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

// Podrijetlo rute

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

// Odredište rute

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

// Međutočke duž rute

//waypoints=optimize:true|-19.9227365, -43.9473546 | -19.9168006, -43.9361124 String waypoints = "waypoints = optimize: true"; for (LatLng point: waypointsList) {waypoints += "|" + točka.duljina + "," + točka.duljina; }

// Osjetnik omogućen

String sensor = "sensor = false";

// Izgradnja parametara web usluge

Parametri niza = str_origin+"&"+str_dest+"&"+senzor+"&"+međutočke;

// Izlazni format

String output = "json";

// Izgradnja URL -a web usluge

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

povratni url;

}

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

@Premosti javnu prazninu 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 (); }

// Crtanje ruta

// Dobivanje URL -a do Google API -ja za upute

Točke popisa = novi ArrayList (); bodova.add (lixeiraB); bodova.add (lixeiraC); bodova.doda (lixeiraD);

String url = getDirectionsUrl (current, lixeiraA, points);

DownloadTask downloadTask = novi DownloadTask (); // Pokretanje preuzimanja json podataka iz API -ja Google Directions downloadTask.execute (url); }

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

Korak 8: Zaključak

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

Preporučeni: