Índice:
- Etapa 1: Conectando O Sensor
- Etapa 2: Montando a Lixeira
- Etapa 3: faça upload para um nuvem
- Etapa 4: Recuperando Dados Do ThingSpeak
- Etapa 5: Criando a Aplicação Android
- Etapa 6: Recuperando O Feed No Android
- Etapa 7: Mostrando No Mapa
- Etapa 8: Conclusão
Vídeo: SmartBin: 8 etapas
2024 Autor: John Day | [email protected]. Última modificação: 2024-01-31 10:23
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
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:
- marcar a posição atual do caminha de lixo
- marcar os pontos correspondentes a cada lixeira no mapa
- 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:
Como fazer uma antena dupla BiQuade 4G LTE Etapas fáceis: 3 etapas
Como Fazer Antena Dupla BiQuade 4G LTE Etapas fáceis: Na maioria das vezes que enfrentei, não tenho uma boa intensidade de sinal para meus trabalhos do dia-a-dia. Então. Procuro e tento diferentes tipos de antena, mas não funciona. Depois de perder tempo, encontrei uma antena que espero fazer e testar, porque não é um princípio de construção
Design de jogo no Flick em 5 etapas: 5 etapas
Design de jogos no Flick em 5 etapas: o Flick é uma maneira realmente simples de fazer um jogo, especialmente algo como um quebra-cabeça, romance visual ou jogo de aventura
Detecção de rosto no Raspberry Pi 4B em 3 etapas: 3 etapas
Detecção de rosto no Raspberry Pi 4B em 3 etapas: Neste Instructable, vamos realizar a detecção de rosto no Raspberry Pi 4 com Shunya O / S usando a Biblioteca Shunyaface. Shunyaface é uma biblioteca de reconhecimento / detecção de rosto. O projeto visa alcançar a detecção mais rápida e velocidade de reconhecimento com
Espelho de cortesia DIY em etapas fáceis (usando luzes de tira LED): 4 etapas
Espelho de toucador DIY em etapas fáceis (usando tiras de LED): Neste post, fiz um espelho de toucador DIY com a ajuda de tiras de LED. É muito legal e você deve experimentá-los também
SmartBin: 4 etapas
SmartBin: O objetivo principal deste projeto é criar um dispositivo eletrônico que use pelo menos um Raspberry Pi. A equipe é composta por 5 futuros engenheiros mecânicos e um engenheiro de automação. Nosso projeto consiste em fazer uma lata de lixo que abre e fecha