Spisu treści:

SmartBin: 8 kroków
SmartBin: 8 kroków

Wideo: SmartBin: 8 kroków

Wideo: SmartBin: 8 kroków
Wideo: Mi Band 8 daje radę! 2024, Listopad
Anonim
SmartBin
SmartBin

Este é um projeto dla inteligentnego systemu coletas, nie ma żadnych innych środowisk lixo recebem dados das lixeiras, identyfikuje ilość prezentowanych lixo w cada uma delas, i uma rota de colet recebem.

Para montar este projeto, é necessário:

  • WęzełMCU
  • Czujnik Ultrassônico de Distancia
  • Caixa de papelão
  • Płyta prototypowa
  • Cabos
  • Urządzenie na Androida

Krok 1: Czujnik Conectando O

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

// definiuje numery pinów #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;

długi mikrosekund = ultradźwięki.timing();

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

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

//Exibe informacoes brak monitora szeregowego

Serial.print("Odległość em cm: ");

Serial.print(cmMsec);

Serial.print(" - Odległość polega na: ");

Serial.println(w milisekundach);

Dane ciągu = Ciąg (cmMsec);

Serial.println(dane);

Krok 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 rozważarmos 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

długi lixeiraID;

pusta pętla () {

lixeiraID = losowo (1, 5);

}

Krok 3: Prześlij 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 parametros, referentes ao volume de cada lixeira.

Aby połączyć się z aplikacją o ThingSpeak, konieczne jest salvar na numer API do canal criado. Siga os passos descritos nie ma oficjalnej strony.

De volta à aplicação, vamos utilizar na biblioteca ESP8266WiFi.h do połączenia z 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("Łączenie z "+ *ssid);

WiFi.begin(ssid, pass);

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

opóźnienie (500);

Serial.print(".");

}

Serial.println("");

Serial.print("Połączenie z ponownym");

Serial.println(ssid);

Serial.print("IP: ");

Serial.println(WiFi.localIP());

}

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

pusta konfiguracja () {

Serial.początek(9600);

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

//Połącz się z Wi-Fi

połączWifi();

}

E, dla enviar os dados dla ThingSpeak, wystarczy połączyć się z HTTP, passando lub numerami z API i dla parametrów.

void sendDataTS(float cmMsec, długi identyfikator){

if (klient.connect(serwer, 80)) {

Serial.println("Enviando dados dla ThingSpeak");

String postStr = apiKey;

postStr += "&field";

postStr += id;

postStr += "=";

postStr += String(cmMsec);

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

Serial.println(postStr);

client.print( POST /aktualizacja

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

client.print("Połączenie: zamknij\n");

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

client.print("Typ treści: application/x-www-form-urlencoded\n");

client.print("Treść-Długość: ");

klient.print(postStr.length());

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

klient.print(postStr);

opóźnienie (1000);

}

klient.stop();

}

O primeiro parâmetro korespondencja à distância em centímetros encontrada pelo sensor ultrassônico. O segundo parametro é o ID da lixeira que foi lida (que foi gerado randomicamente, um número de 1 a 4).

O ID da lixeira służyć também para identificar para qual campo será feito o upload do valor lido.

Krok 4: Recuperando Dados Czy ThingSpeak

O ThingSpeak allowe efetuar leitura dos dados do seu canal, através de um serviço retornando um JSON. Jako 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 brak linku informado previamente. Os mais Importantes para o projekto são:

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

Esta to URL que será lida do aplicativa Android, para recuperar os dados do ThingSpeak.

Krok 5: Criando a Aplicação Android

Nie Android Studio, wołaj nowy projekt Androida. Para o correto funcionamento da aplicação, é necessário configurar as permissões abaixo no AndroidManifest.

Aby korzystać z Google Maps, konieczne jest korzystanie z serwisu Google. Siga os passos descritos no link Obter chave de API.

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

Klucz API dla interfejsów API opartych na Mapach Google jest zdefiniowany jako zasób tekstowy.

(Zobacz plik „res/values/google_maps_api.xml”).

Pamiętaj, że klucz API jest powiązany z kluczem szyfrowania używanym do podpisywania APK. Potrzebujesz innego klucza API dla każdego klucza szyfrowania, w tym klucza wydania używanego do podpisywania APK do publikacji. Możesz zdefiniować klucze dla celów debugowania i wydania w src/debug/ i src/release/.

<meta-dane

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

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

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

n

Krok 6: Recuperando O Feed bez Androida

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:

JSONOdpowiedź obiektuLixeiraA; JSONOdpowiedź obiektuLixeiraB; JSONOdpowiedź obiektuLixeiraC; JSONOdpowiedź obiektuLixeiraD;

Para brir conexão com jako adresy URL, vamos usar criar uma classe auxiliar, chamada HttpJsonParser. Esta classe será responsável por brir uma conexão com um URL, efetuar leitura dos dados encontrados, i retornar lub objeto JSON montado.

public JSONObject makeHttpRequest(String url, String method, Map params) {

próbować {

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

}

if ("GET".equals(metoda)) { url = url + "?" + zakodowane parametry; urlObj = nowy adres URL(url); urlConnection = (HttpURLConnection) urlObj.openConnection(); urlConnection.setRequestMethod(metoda);

} w przeciwnym razie {

urlObj = nowy URL(url); urlConnection = (HttpURLConnection) urlObj.openConnection(); urlConnection.setRequestMethod(metoda); urlConnection.setRequestProperty("Typ-treści", "aplikacja/x-www-form-urlencoded"); urlConnection.setRequestProperty("Długość treści", String.valueOf(encodedParams.getBytes().length)); urlConnection.getOutputStream().write(encodedParams.getBytes()); } //Połącz z serwerem urlConnection.connect(); //Odczytaj odpowiedź to = urlConnection.getInputStream(); Czytnik BufferedReader = nowy BufferedReader(nowy InputStreamReader(is)); StringBuilder sb = nowy StringBuilder(); Linia sznurka;

//Przeanalizuj odpowiedź

while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } jest blisko(); json = sb.toString(); //Konwertuj odpowiedź na obiekt 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("JSON Parser", "Błąd parsowania danych " + e.toString()); } catch (Exception e) { Log.e("Wyjątek", "Błąd parsowania danych " + e.toString()); }

// zwróć obiekt JSON

zwróć jObj;

}

}

De volta a atividade principal, vamos efetuar and chamada jako adresy URL forma assíncrona, escrevendo este código dentro do metody 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);

zwróć null;}

Quando lub metody doInBackground encerrado, lub kontroli wykonywania Android passa lub metody onPostExecute. Neste método, vamos criar os objetos Lixeira, e popular com os dados recuperados do ThingSpeak:

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

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

Widok mainView =(Widok)findViewById(R.id.activity_main); if (success == 1) { try { //Cria feedDetail para cada lixeira Lixeira feedDetails1 = new Lixeira(); Lixeira feedDetails2 = nowa Lixeira(); Lixeira feedDetails3 = nowa Lixeira(); Lixeira feedDetails4 = nowa 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 = nowy SmartBinService(); kalkulator.montaListaLixeiras(Lista kanałów);

//Komponenty Recupera

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

//Dane rzeczywiste

Data currentTime = Kalendarz.getInstance().getTime(); SimpleDateFormat simpleDate = new SimpleDateFormat("dd/MM/rrrr"); String currentDate = simpleDate.format(currentTime); createDate.setText(KEY_DATE + bieżąca data + " "); listDeLixeiras.setAdapter(adapter);

} catch (JSONException e) {

e.printStackTrace(); }

} w przeciwnym razie {

Toast.makeText(MainActivity.this, "Wystąpił błąd podczas ładowania danych", Toast. LENGTH_LONG).show();

}

} }); }

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

Krok 7: Mostrando bez mapy

Mostrando Nie Mapa
Mostrando Nie Mapa

Ainda na dyrektora atividade, vamos adicionar uma ação i ser relacionada z botão Mapa, tea inicial.

/** Wywoływane, gdy użytkownik naciśnie przycisk Mapa */ public void openMaps(View view) { Intent intent = new Intent(this, LixeiraMapsActivity.class);

//Przekaż listę de lixeiras

Pakiet pakietu = nowy pakiet(); bundle.putParcelableArrayList("lixeiras", feedList); intent.putDodatki(pakiet);

startActivity(zamiar);

}

Brak mapy, temos três atividades a executar:

  1. Marcar a posição atual do caminha de lixo
  2. marcar os pontos korespondentes a cada lixeira bez mapy
  3. Traçar a rota entre os pontos

Aby wykonać passos acima, użyj API Google Directions. Para desenhar as rotas, foram seguidos os passos do tutorial Rysowanie wskazówek dojazdu między dwoma lokalizacjami za pomocą Google Directions w Google Map Android API V2

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

//Lokalizacje

prąd prywatny LatLng;

prywatna LatLng lixeiraA; prywatny LatLng lixeiraB; prywatny LatLng lixeiraC; prywatny LatLng lixeiraD;.

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

private void checkLocationandAddToMap() { //Sprawdzenie, czy użytkownik przyznał uprawnienia 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) { //Żądanie uprawnień do lokalizacji ActivityCompat.requestPermissions(ten, nowy String{android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); powrót; }

//Pobieranie ostatniej znanej lokalizacji za pomocą Fus

Lokalizacja lokalizacji = LocationServices. FusedLocationApi.getLastLocation(googleApiClient);

//MarkerOptions są używane do tworzenia nowego Markera. Możesz określić lokalizację, tytuł itp. za pomocą MarkerOptions

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

//Dodanie utworzonego znacznika na mapie, przesunięcie kamery na pozycję

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

// Natychmiast przenieś aparat do lokalizacji z 15-krotnym powiększeniem.

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

// Powiększanie, animowanie kamery.

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

}

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

private void addBinALocation() { //Sprawdzenie, czy użytkownik przyznał uprawnienia 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) { //Żądanie uprawnień do lokalizacji ActivityCompat.requestPermissions(this, new String{android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); powrót; }

//Praca da Estação

podwójna szerokość geograficzna = -19,9159578; podwójna długość geograficzna = -43,9387856; this.lixeiraA = new LatLng(szerokość, długość geograficzna);

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

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

Jako 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 as rotas entre os pontos. Para tal, um conceito muito ważne, 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){

// Początek trasy

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

// Miejsce docelowe trasy

String str_dest = "docelowa="+docelowa.szerokość geograficzna+", "+docelowa.długość geograficzna;

//Punkty na trasie

//waypoints=optimize:true|-19.9227365, -43.9473546|-19.9168006, -43.9361124 String waypoints = "waypoints=optimize:true"; for (LatLng point: waypointsList){ waypoints += "|" + punkt.szerokość + ", " + punkt.długość geograficzna; }

// Czujnik włączony

Czujnik ciągu = "czujnik=fałsz";

// Budowanie parametrów do usługi sieciowej

Parametry ciągu = str_origin+"&"+str_dest+"&"+czujnik + "&" + punkty;

// Format wyjściowy

Wyjście ciągu = "json";

// Budowanie adresu URL do usługi internetowej

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

zwrotny adres URL;

}

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

@Zastąp public void onMapReady(GoogleMap googleMap) { mMap = googleMap;

sprawdźLokalizacjaiDodajDoMapy();

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

//Narysuj trasy

// Pobieranie adresu URL do interfejsu Google Directions API

Lista punktów = new ArrayList(); points.add(lixeiraB); points.add(lixeiraC); points.add(lixeiraD);

URL ciągu = getDirectionsUrl(bieżący, lixeiraA, punkty);

DownloadTask downloadTask = nowy DownloadTask(); // Rozpocznij pobieranie danych json z Google Directions API downloadTask.execute(url); }

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

Krok 8: Podsumowanie

Este foi um projeto trabalhando conceitos 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 wideo do pełnego projektu, para ilustração, e os fontes das atividades criadas bez Androida.

Zalecana: