Taula de continguts:
- Pas 1: Conectant el sensor O
- Pas 2: Montant a Lixeira
- Pas 3: pengeu Para a Nuvem
- Pas 4: Recuperant Dados Do ThingSpeak
- Pas 5: Criant a l'aplicació d'Android
- Pas 6: Recuperant O Feed sense Android
- Pas 7: Mostrando No Mapa
- Pas 8: Conclusió
Vídeo: SmartBin: 8 passos
2024 Autora: John Day | [email protected]. Última modificació: 2024-01-31 10:17
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
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:
- marcar a posição atual do caminha de lixo
- marcar els ponts corresponents a cada lixeira no mapa
- 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:
Disseny de jocs en Flick en 5 passos: 5 passos
Disseny de jocs en Flick en 5 passos: Flick és una manera molt senzilla de fer un joc, sobretot com un trencaclosques, una novel·la visual o un joc d’aventures
Detecció de cares a Raspberry Pi 4B en 3 passos: 3 passos
Detecció de cares a Raspberry Pi 4B en 3 passos: en aquest manual, farem la detecció de cares a Raspberry Pi 4 amb Shunya O / S mitjançant la biblioteca Shunyaface. Shunyaface és una biblioteca de reconeixement / detecció de cares. El projecte té com a objectiu aconseguir una velocitat de detecció i reconeixement més ràpida amb
Com fer un comptador de passos ?: 3 passos (amb imatges)
Com fer un comptador de passos ?: Jo solia tenir un bon rendiment en molts esports: caminar, córrer, anar en bicicleta, jugar a bàdminton, etc. M’encanta viatjar poc després. Bé, mireu el meu ventre corpulent … Bé, de totes maneres, decideixo tornar a començar a fer exercici. Quin equip he de preparar?
Mirall de vanitat de bricolatge en passos senzills (amb llums de tira LED): 4 passos
Mirall de vanitat de bricolatge en passos senzills (amb llums de tires LED): en aquest post vaig crear un mirall de vanitat de bricolatge amb l'ajut de les tires LED. És molt genial i també heu de provar-les
SmartBin: 4 passos
SmartBin: l'objectiu principal d'aquest projecte és crear un dispositiu electrònic que utilitzi almenys un Raspberry Pi. L’equip està format per cinc futurs enginyers mecànics i un enginyer automatitzat. El nostre projecte consisteix a fer una paperera que s’obre i tanca