תוכן עניינים:

SmartBin: 8 שלבים
SmartBin: 8 שלבים

וִידֵאוֹ: SmartBin: 8 שלבים

וִידֵאוֹ: SmartBin: 8 שלבים
וִידֵאוֹ: Первый обзор Mi Band 8 — лучший Band в истории! 2024, נוֹבֶמבֶּר
Anonim
SmartBin
SmartBin

Este é um projeto para um system inteligente de coletas, no qual os caminhões de lixo recebem dados das lixeiras, זיהוי ו 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, וזה הכרחי:

  • NodeMCU
  • חיישן Ultrassônico de Distancia
  • Caixa de papelão
  • פרוטובארד
  • קאבוס
  • אנדרואיד Dispositivo

שלב 1: חיישן Conectando O

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:

// מגדיר מספרי סיכות #הגדר pino_trigger 2 // D4

#define pino_echo 0 // D3

אם אתה יכול להשתמש בחיישן או במגוון רחב של הדרכות, או לפרופיל של FilipeFlop, disponível aqui.

צף cmMsec, inMsec;

microsec ארוך = ultrasonic.timing ();

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

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

// מידע Exibe אינו מספק צג סדרתי

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

Serial.print (cmMsec);

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

Serial.println (inMsec);

נתוני מחרוזת = מחרוזת (cmMsec);

Serial.println (נתונים);

שלב 2: Montando a Lixeira

אגורה, vamos montar a lixeira inteligente. Precisaremos conectar o sensor ultrassônico no “teto” da lixeira. לדוגמא, לנצל את כל זה. Em seguida, temos que medir a distância inicial, para saber o valor para a lixeira vazia. אין מאו קאסו, foi de 26, 3 ס מ. Esse é o valor que considerarmos 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

lixeiraID ארוך;

לולאת חלל () {

lixeiraID = אקראי (1, 5);

}

שלב 3: העלה את Para a Nuvem

Agora, precisamos enviar estes dados para a nuvem. Eu escolhi o ThingSpeak, מאת מוכר com o mesmo. Primeiramente, é necessário criar um novo canal, recebendo 4 parâmetros, referentes ao volume de cada lixeira.

ניתן להשתמש באפליקציית ThingSpeak, בהכרח יש צורך בממשק API של תעלות. Siga os passos descritos אין אתר רשמי.

De volta à aplicaçã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 identifierador e a senha de sua rede).

void connectWifi () {

Serial.print ("מתחבר ל-"+ *ssid);

WiFi.begin (ssid, pass);

בעוד (WiFi.status ()! = WL_CONNECTED) {

עיכוב (500);

Serial.print (".");

}

Serial.println ("");

Serial.print ("Conectado na rede");

Serial.println (ssid);

Serial.print ("IP:");

Serial.println (WiFi.localIP ());

}

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

הגדרת בטל () {

Serial.begin (9600);

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

// Conectando ao Wi-Fi

connectWifi ();

}

E, על מנת להדגיש את זה עבור ThingSpeak, אנו יכולים להשתמש ב- HTTP דרך, להעביר את המודעות והמשתמשים שלנו.

void sendDataTS (צף cmMsec, מזהה ארוך) {

if (client.connect (שרת, 80)) {

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

מחרוזת postStr = apiKey;

postStr += "& field";

postStr += id;

postStr += "=";

postStr += מחרוזת (cmMsec);

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

Serial.println (postStr);

client.print ("POST /עדכון HTTP /1.1 / n");

client.print ("מארח: api.thingspeak.com / n");

client.print ("חיבור: סגור / n");

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

client.print ("סוג תוכן: application/x-www-form-urlencoded / n");

client.print ("אורך התוכן:");

client.print (postStr.length ());

client.print ("\ n / n");

client.print (postStr);

עיכוב (1000);

}

client.stop ();

}

O primeiro parâmetro correspondonde à distância em centímetros encontrada pelo sensor 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 serve também for identifar para qual campo será feito o upload do valor lido.

שלב 4: Recuperando Dados Do ThingSpeak

O ThingSpeak permite efetuar leitura dos dados do seu canal, através de um serviço retornando um JSON. כפי שניתן להבחין במאפיינים אחרים על מנת להזין את תעלת הסו אסטאו דסקריטס אקווי:

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 informado previamente. הדברים החשובים ביותר עבור הפרוייקט סאו:

  • CHANNEL_ID: ערוץ número do seu
  • FIELD_NUMBER: o número do campo
  • API_KEY: תעלת API do seu

כתובת האתר יכולה להוביל לאפליקציות אנדרואיד, לשחזור תוכנות ThingSpeak.

שלב 5: Criando ו- Aplicação Android

אין אנדרואיד סטודיו, crie um novo projeto Android. Para o correto funcionamento da aplicação, ואני צריך להגדיר את ההרשאות כי לא ניתן להשתמש ב- AndroidManifest.

עבור שימוש במפות 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 לממשקי API מבוססי מפות Google מוגדר כמשאב מחרוזת.

(עיין בקובץ "res/values/google_maps_api.xml").

שים לב שמפתח ה- API מקושר למפתח ההצפנה המשמש לחתימת ה- APK. אתה צריך מפתח API אחר לכל מפתח הצפנה, כולל מפתח השחרור המשמש לחתימת ה- APK לפרסום. באפשרותך להגדיר את המפתחות של מטרות באגים ושחרורים ב- src/debug/ו- src/release/.

<meta-data

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

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

תצורה מלאה של אנדרואיד מניפסט יישומי ao projeto.

נ

שלב 6: Recuperando O Feed ללא אנדרואיד

עיקר מנהלי האנדרואיד, MainActivity, קריא 4 משתנים עבור זיהוי של כל דבר לעשות דבר דבר:

מחרוזת פרטית url_a = "https://api.thingspeak.com/channels/429823/fields/1/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; מחרוזת פרטית url_b = "https://api.thingspeak.com/channels/429823/fields/2/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; מחרוזת פרטית url_c = "https://api.thingspeak.com/channels/429823/fields/3/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true"; מחרוזת פרטית url_d = "https://api.thingspeak.com/channels/429823/fields/4/last.json?api_key="+API_THINGSPEAK_KEY+"&status=true";

אם אתה יכול להשתמש באתרים אחרים, ניתן להשתמש במכשיר לקלאסה לאנדרואיד, ל- JSONObject. Mais uma vez, vamos criar um objeto para cada URL:

תגובת JSONObjectLixeiraA; תגובת JSONObjectLixeiraB; JSONObject responseLixeiraC; תגובת JSONObjectLixeiraD;

Para abrir a conexão com כתובות אתרים, 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 or objeto JSON montado.

public JSONObject makeHttpRequest (כתובת אתר מחרוזת, שיטת מחרוזת, פרמאות מפות) {

נסה {

בונה Uri. Builder = Uri. Builder חדש (); URL urlObj; String encodedParams = ""; if (params! = null) {for (entry. Map. Entry: params.entrySet ()) {builder.appendQueryParameter (entry.getKey (), entry.getValue ()); }} if (builder.build (). getEncodedQuery ()! = null) {encodedParams = builder.build (). getEncodedQuery ();

}

if ("GET". שווה (שיטה)) {url = url + "?" + encodedParams; urlObj = כתובת אתר חדשה (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod (שיטה);

} אחר {

urlObj = כתובת אתר חדשה (url); urlConnection = (HttpURLConnection) urlObj.openConnection (); urlConnection.setRequestMethod (שיטה); urlConnection.setRequestProperty ("סוג תוכן", "application/x-www-form-urlencoded"); urlConnection.setRequestProperty ("Content-Length", String.valueOf (encodedParams.getBytes (). length)); urlConnection.getOutputStream (). לכתוב (encodedParams.getBytes ()); } // התחבר לשרת urlConnection.connect (); // קראו שהתגובה היא = urlConnection.getInputStream (); קורא BufferedReader = BufferedReader חדש (InputStreamReader חדש (is)); StringBuilder sb = new StringBuilder (); קו מחרוזת;

// לנתח את התגובה

while ((line = reader.readLine ())! = null) {sb.append (שורה + "\ n"); } קרוב(); json = sb.toString (); // המר את התגובה לאובייקט JSON jObj = JSONObject חדש (json);

} catch (UnsupportedEncodingException e) {

e.printStackTrace (); } catch (ProtocolException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } catch (JSONException e) {Log.e ("JSON Parser", "שגיאה בניתוח נתונים" + e.toString ()); } catch (חריגה ה) {Log.e ("חריגה", "שגיאה בניתוח נתונים" + e.toString ()); }

// החזר אובייקט JSON

החזר jObj;

}

}

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

מחרוזת מוגנת @Override doInBackground (מחרוזות …) {HttpJsonParser jsonParser = 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);

להחזיר null;}

Quando o método doInBackgroundé encerrado, o control de execução to Android passas for método onPostExecute. Neste método, vamos criar os objetos Lixeira, e popular com os dados recuperados do ThingSpeak:

חלל מוגן onPostExecute (תוצאה מחרוזת) {pDialog.dismiss (); runOnUiThread (new Runnable () {public void run () {

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

הצג mainView = (הצג) findViewById (R.id.activity_main); if (success == 1) {try {// Cria feedDetail para cada lixeira Lixeira feedDetails1 = Lixeira חדש (); Lixeira feedDetails2 = Lixeira חדש (); Lixeira feedDetails3 = Lixeira חדש (); Lixeira feedDetails4 = 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

מחשבון SmartBinService = SmartBinService חדש (); מחשבון.montaListaLixeiras (feedList);

// רכיבי Recupera

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

// נתונים atual

תאריך currentTime = Calendar.getInstance (). GetTime (); SimpleDateFormat simpleDate = חדש SimpleDateFormat ("dd/MM/yyyy"); מחרוזת currentDate = simpleDate.format (currentTime); createDate.setText (KEY_DATE + currentDate + ""); listaDeLixeiras.setAdapter (מתאם);

} catch (JSONException e) {

e.printStackTrace (); }

} אחר {

Toast.makeText (MainActivity.this, "אירעה שגיאה במהלך טעינת הנתונים", Toast. LENGTH_LONG).show ();

}

} }); }

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

שלב 7: Mostrando No Mapa

מוסטרנדו אין מאפה
מוסטרנדו אין מאפה

מנהלת מנהיגות עו ד, ואנו יכולים להעריך גם את זה, אך לא רשמי.

/ ** מתקשר כאשר המשתמש מקיש על כפתור Mapa*/ public void openMaps (תצוגת תצוגה) {Intent intention = new Intent (this, LixeiraMapsActivity.class);

// Passa a list de lixeiras

צרור צרור = צרור חדש (); bundle.putParcelableArrayList ("lixeiras", feedList); intention.putExtras (חבילה);

startActivity (כוונה);

}

אין מפה, זמנית של התנהגות מבצעת:

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

בשביל להפעיל אותנו, אנחנו יכולים להשתמש ב- API של Google. Para desenhar as rotas, foram seguidos os passos do do tutorial ציור מסלולי נסיעה בין שני מיקומים באמצעות כיווני Google במפת Google Android Android V2

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

// מיקומים

זרם LatLng פרטי;

פרטי LatLng lixeiraA; lixeiraB פרטי LatLng; lixeiraC פרטי LatLng; פרטי LatLng lixeiraD;.

לפרטים נוספים על posição atual no mapa, foi criado o método:

private void checkLocationandAddToMap () {// בודק אם המשתמש נתן את ההרשאה אם (ActivityCompat.checkSelfPermission (this, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (Android. ACCESS_COARSE_LOCATION)! = PackageManager. PERMISSION_GRANTED) {// בקשת הרשאת מיקום ActivityCompat.requestPermissions (זה, מחרוזת חדשה {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); לַחֲזוֹר; }

// אחזור המיקום האחרון הידוע באמצעות ה- Fus

מיקום מיקום = LocationServices. FusedLocationApi.getLastLocation (googleApiClient);

// MarkerOptions משמשים ליצירת סמן חדש. ניתן לציין מיקום, כותרת וכו 'באמצעות MarkerOptions

this.current = LatLng חדש (location.getLatitude (), location.getLongitude ()); MarkerOptions markerOptions = MarkerOptions חדש (). מיקום (הנוכחי).title ("Posição atual");

// הוספת הסמן שנוצר על המפה, העברת המצלמה למיקום

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

// העבר את המצלמה באופן מיידי למיקום עם זום של 15.

mMap.moveCamera (CameraUpdateFactory.newLatLngZoom (הנוכחי, 15));

// התקרב, אנימציה של המצלמה.

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

}

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

private void addBinALocation () {// בודק אם המשתמש נתן את ההרשאה אם (ActivityCompat.checkSelfPermission (this, android. Manifest.permission. ACCESS_FINE_LOCATION)! = PackageManager. PERMISSION_GRANTED && ActivityCompat.checkSelfPermission (זה, אנדרואיד. ACCESS_COARSE_LOCATION)! = PackageManager. PERMISSION_GRANTED) {// בקשת הרשאת מיקום ActivityCompat.requestPermissions (זה, מחרוזת חדשה {android. Manifest.permission. ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); לַחֲזוֹר; }

// Praça da Estação

קו רוחב כפול = -19.9159578; אורך כפול = -43.9387856; this.lixeiraA = LatLng חדש (קו רוחב, קו אורך);

MarkerOptions markerOptions = MarkerOptions חדש (). מיקום (lixeiraA).title ("Lixeira A");

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

כמו posições de latitude e longitude de cada lixeira foram recuperadas através do próprio Maps Google, e deixadas fixas no código. Idealmente, estes valores ficariam salvos em um banco de dados (לדוגמא Firebase). Será a primeira evolução deste projeto!

O último passo agora é traçar as rotas entre os pontos. אם כן, חשוב לי להעריך את זה, ולהשתמש בנושאים הבאים, או בנקודות ציון!

Foi criado um método para traçar a rota entre dois dados pontos:

private String getDirectionsUrl (מקור LatLng, LatLng dest, רשימת נקודות ציון) {

// מקור המסלול

מחרוזת str_origin = "origin ="+origin.latitude+","+origin.longitude;

// יעד המסלול

מחרוזת str_dest = "destination ="+dest.latitude+","+dest.longitude;

// נקודות ציון לאורך המסלול

//waypoints=optimize:true|-19.9227365, -43.9473546 | -19.9168006, -43.9361124 String waypoints = "waypoints = optimize: true"; עבור (נקודת LatLng: waypointsList) {נקודות ציון += "|" + point.latitude + "," + point.longitude; }

// חיישן מופעל

חיישן מחרוזת = "חיישן = שקר";

// בניית הפרמטרים לשירות האינטרנט

פרמטרים של מחרוזות = str_origin+"&"+str_dest+"&"+חיישן+"&"+נקודות ציון;

// פורמט פלט

פלט מחרוזת = "json";

// בניית כתובת ה- URL לשירות האינטרנט

כתובת URL = "https://maps.googleapis.com/maps/api/directions/"+output+"?"+ פרמטרים; System.out.println ("++++++++++++++"+כתובת אתר);

כתובת אתר החזרה;

}

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

@ביטול חלל ציבורי onMapReady (GoogleMap googleMap) {mMap = googleMap;

checkLocationandAddToMap ();

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

// צייר מסלולים

// קבלת כתובת ה- URL ל- Google Directions

נקודות רשימה = ArrayList חדש (); points.add (lixeiraB); points.add (lixeiraC); points.add (lixeiraD);

כתובת אתר מחרוזת = getDirectionsUrl (הנוכחי, lixeiraA, נקודות);

DownloadTask downloadTask = NewTask Download (); // התחל להוריד את נתוני json מ- Google Directions API downloadTask.execute (url); }

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

שלב 8: מסקנה

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ões sem interferência humana direta. אם אתה יכול לעשות זאת, תוכל להשלים את הפעולות האלה, למשל, או להשתמש בפונטים לאנדרואיד.

מוּמלָץ: