Sadržaj:
- Korak 1: Conectando O osjetnik
- Korak 2: Montando a Lixeira
- Korak 3: Prenesite Para a Nuvem
- Korak 4: Recuperando Dados Do ThingSpeak
- Korak 5: Criando i Aplicação Android
- Korak 6: Recuperando O Feed Nema Androida
- Korak 7: Mostrando bez karte
- Korak 8: Zaključak
Video: SmartBin: 8 koraka
2024 Autor: John Day | [email protected]. Zadnja promjena: 2024-01-31 10:20
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
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:
- marcar a posição atual do caminha de lixo
- marcar os pontos korespondentes a cada lixeira no mapa
- 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:
Dizajn igre brzim pokretom u 5 koraka: 5 koraka
Dizajn igre u Flick -u u 5 koraka: Flick je zaista jednostavan način stvaranja igre, osobito nečega poput zagonetke, vizualnog romana ili avanturističke igre
Broj koraka: 17 koraka
الكشف عن عن أنواع المحاليل: محمدآل سعودالكشف عن المحاليل رابط الفديو
Prepoznavanje lica na Raspberry Pi 4B u 3 koraka: 3 koraka
Prepoznavanje lica na Raspberry Pi 4B u 3 koraka: U ovom Instructableu ćemo izvršiti detekciju lica na Raspberry Pi 4 sa Shunya O/S pomoću knjižnice Shunyaface. Shunyaface je biblioteka za prepoznavanje/otkrivanje lica. Cilj projekta je postići najbržu brzinu otkrivanja i prepoznavanja s
Kako napraviti brojač koraka?: 3 koraka (sa slikama)
Kako napraviti brojač koraka?: Nekada sam se dobro snašao u mnogim sportovima: hodanje, trčanje, vožnja bicikla, igranje badmintona itd. Volim jahanje da bih brzo putovao. Pa, pogledaj moj trbušni trbuh … Pa, u svakom slučaju, odlučujem ponovno početi vježbati. Koju opremu trebam pripremiti?
SmartBin: 4 koraka
SmartBin: Glavna svrha ovog projekta je stvaranje elektroničkog uređaja koji koristi barem jedan Raspberry Pi. Tim čini 5 budućih strojarskih inženjera i jedan inženjer automatizacije. Naš projekt sastoji se u izradi kante za smeće koja se otvara i zatvara