Skocz do zawartości

Przeszukaj forum

Pokazywanie wyników dla tagów 'arduino'.

  • Szukaj wg tagów

    Wpisz tagi, oddzielając przecinkami.
  • Szukaj wg autora

Typ zawartości


Kategorie forum

  • Elektronika i programowanie
    • Elektronika
    • Arduino, ESP
    • Mikrokontrolery
    • Raspberry Pi
    • Inne komputery jednopłytkowe
    • Układy programowalne
    • Programowanie
    • Zasilanie
  • Artykuły, projekty, DIY
    • Artykuły redakcji (blog)
    • Artykuły użytkowników
    • Projekty - roboty
    • Projekty - DIY
    • Projekty - DIY (początkujący)
    • Projekty - w budowie (worklogi)
    • Wiadomości
  • Pozostałe
    • Oprogramowanie CAD
    • Druk 3D
    • Napędy
    • Mechanika
    • Zawody/Konkursy/Wydarzenia
    • Sprzedam/Kupię/Zamienię/Praca
    • Inne
  • Ogólne
    • Ogłoszenia organizacyjne
    • Dyskusje o FORBOT.pl
    • Na luzie
    • Kosz

Szukaj wyników w...

Znajdź wyniki, które zawierają...


Data utworzenia

  • Rozpocznij

    Koniec


Ostatnia aktualizacja

  • Rozpocznij

    Koniec


Filtruj po ilości...

Data dołączenia

  • Rozpocznij

    Koniec


Grupa


Znaleziono 116 wyników

  1. Witam, Chciałbym przedstawić mój projekt, którym jest skaner 3D. Niestety jego dokładność nie pozwala na skanowanie obiektów w celu tworzenia modeli, ale można przy jego pomocy stworzyć poglądową mapę głębokości otoczenia. Skaner ten powstał głównie w celu sprawdzenia możliwości aktywnej komunikacji z Arduino z Komputerem przy użyciu Pythona. Zasada działania Skaner ten jest obsługiwany przez urządzenie zewnętrzne które łączy się z Arduino Uno za pośrednictwem USB. Składa się on z sensora ultradźwiękowego, który porusza się w dwóch osiach za pomocą serwomechanizmów - dokładnie tych. Całość jest zamocowana na czworonożnej ramie która została wydrukowana w 3D. Działanie Arduino podlega sterowaniu zewnętrznemu, a samo ma zdefiniowane jedynie podstawowe funkcje które są wywoływane za pośrednictwem komunikacji przez UART. Funkcje zawarte w Arduino dotyczą obrotu czujnika w dwóch osiach oraz wykonania pomiaru odległości i zwrócenia wyniku na połączenie szeregowe. Jak już wspomniano wcześniej, od strony komputera komunikacja realizowana jest za pomocą języka Python. Komputer wysyła polecenia dotyczące wykonania ruchu w danej osi oraz zwrotu informacji o mierzonej odległości. Niestety z powodu długiego czasu trwania pomiaru, wrażliwości sensora na wibracje, oraz wykonywaniu za każdym razem kilku pomiarów w celu minimalizacji błędów, mapowanie dużego obszaru z dużą rozdzielczością potrafi zajmować nawet kilkanaście minut - maksymalna rozdzielczość wynosi 180x180 co daje 32400 pomiarów. Arduino Kod Ardunio jest dość prosty i krótki, funkcje ograniczają się, zgodnie z tym co napisano powyżej, do obsługi serw oraz wykonywaniu pomiarów. W tym przypadku wykonywane są 3 pomiary a na linię portu szeregowego wysyłana jest wartość uśredniona. #include <Servo.h> #include <Wire.h> const int trigPin = 9; const int echoPin = 10; long duration; int distance1; int distance2; int distance3; String in = "1"; #define LED 13 int pos=105; int pos1=50; int th=1.5; int inn; Servo x; Servo y; void setup() { Serial.setTimeout(100); pinMode(LED, OUTPUT); digitalWrite (LED, LOW); x.attach(3); y.attach(6); x.write(pos); y.write(pos1); pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); Serial.begin(9600); } void loop() { x.write(pos); y.write(pos1); if (Serial.available()){ delay(10); if (Serial.available()>0) { in = Serial.read(); // Serial.write(in); inn = in.toInt(); } if (inn==1){ measure(); Serial.println((distance1+distance2+distance3)/3); } if (inn==2){ pos++; } if (inn==3){ pos--; } if (inn==4){ pos1++; } if (inn==5){ pos1--; } if (inn>10 and inn<105){ pos = inn; } if (inn>105){ pos1 = inn-105; } } } void measure(){ digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distance3= duration*0.034/2; if (distance3 > 79) distance3 = 80; delayMicroseconds(1000); digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distance2= duration*0.034/2; if (distance2 > 79) distance2 = 80; delayMicroseconds(1000); digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); duration = pulseIn(echoPin, HIGH); distance1= duration*0.034/2; if (distance1 > 79) distance1 = 80; //Serial.print("bare average=");Serial.println((distance1+distance2+distance3)/3); if ((distance1 > (distance2+distance3)/2 + th) or (distance1 < (distance2+distance3)/2 - th)){ distance1=(distance2+distance3)/2; }else if((distance2 > (distance1+distance3)/2 + th) or (distance2 < (distance1+distance3)/2 - th)){ distance2=(distance1+distance3)/2; }else if((distance3 > (distance1+distance2)/2 + th) or (distance3 < (distance1+distance2)/2 - th)){ distance3=(distance1+distance2)/2; } return distance1, distance2, distance3; Gdy Arduino odbierze wiadomość '1' wykona pomiar i zwróci jego wartość, gdy otrzyma wartość wyższą, wykona odpowiedni ruch wybranym serwem. Wartości większe niż 10 służą do ustawienia pozycji początkowej, i mogą umieścić czujnik jedynie w jednej ćwiartce pola roboczego. Python Działanie funkcji sterujących jest bardziej rozbudowane. Główny skrypt ma następującą postać. import sys import os import sonarLib as sn import matplotlib.pyplot as plt import numpy as np import smooth import index sn.resolution(index.y,index.x) sn.step(index.s) sn.smth(index.sm) sn.get() for i in range(sn.smoothness): smooth.exe(sn.r,3) sn.r = np.flip(sn.r,1) plt.imsave('img.png',sn.r,cmap='gray') os.system('show.py') Na początku wyświetlane jest prymitywne GUI - index.py stworzone przy pomocy matplotlib.pyplot. Wybrane parametry pracy są określane i przy pomocy sn.get() inicjowane są wszystkie skrypty służące do wykonania pomiarów i przetworzenia danych w macierz. Gdy długotrwały proces dobiegnie końca, obraz jest delikatnie wygładzany w celu usunięcia pikseli których wartości nie pasują do otoczenia. Częste było również występowanie artefaktów w postaci poziomych linii o dość jednolitych wartościach. Jak poniżej. (To jakiś wysoki wąski przedmiot na biurku.) Jest to "surowe" zdjęcie przed filtracją. Po filtracji zdjęcie jest wyświetlane w GUI które umożliwia na interaktywny dobór progu w celu wybrania pikseli znajdujących się bliżej niż zadana wartość. Jeśli ktoś jest ciekawy działania całego programu, to zamieszczam go tutaj: Skaner3D.rar Ostrzegam jednak, że może on być prymitywny i miejscami chaotyczny . Działanie Poniżej jedno z najlepszych "ujęć" razem z widokiem z aparatu z tej samej perspektywy. (Nie miałem oryginalnego pliku i jest to ss wykonany telefonem.) To chyba już wszystko co można w skrócie powiedzieć o tym projekcie. Mam nadzieje, że może się podobać. Gdybym miał coś w nim poprawić, na pewno zmieniłbym rodzaj czujnika na IR oraz spróbował upłynnić ruchy napędów. Poniżej jeszcze zdjęcie całego skanera od góry. Pozdrawiam, i czekam na pytania i porady.
  2. Witam Pisze ponieważ nie mogę poradzić sobie z błędem który dostaje podczas podłączania modułu bluetooth XM-15. Przeglądałem strony w poszukiwaniu jakieś pomocy nie udało mi się tego niestety znaleźć. Jestem nowy wiec odrazu rzuciłem się na głęboka wodę nie sprawdzając tego moduły. Konfiguracja polegała na tym że podłączyłem moduł do arduino i działało to ok. Podczas pisania programów miałem problem z uruchomieniem monitora portu szeregowego. Błąd mówił ze port jest zajęty. Zacząłem szukać po internecie co to może być i znalazłem ,że może to być poprostu złe dobrana prędkość. Więc spróbowałem ja zmieniać na 9600. Na forum jest pokazane jak zrobić to z modelem HC za pomocą polecenia AT. Napisane jest tam ze po wpisaniu AT powinno wyskoczyć " AT ok ". U mnie wyskakuje " Witaj AT ! ", pisałem jakiś prosty program który miał właśnie tak dziać, że pisze coś i on ma wyświetlać Witaj + "coś "+ !. Teraz napisałem prosty program aby wgl sprawdzić czy jakoś ten moduł mogę podłączyć i dostaje błąd problemu z wgrywaniem na płytkę. Wygląda on tak : avrdude: Version 6.3-20171130 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ Copyright (c) 2007-2014 Joerg Wunsch System wide configuration file is "C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.19.0_x86__mdqgnx93n4wtt\hardware\tools\avr/etc/avrdude.conf" Using Port : COM4 Using Programmer : arduino Overriding Baud Rate : 115200 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 1 of 10: not in sync: resp=0xe2 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 2 of 10: not in sync: resp=0xe2 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 3 of 10: not in sync: resp=0xe2 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 4 of 10: not in sync: resp=0xe2 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 5 of 10: not in sync: resp=0xe2 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 6 of 10: not in sync: resp=0xe2 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 7 of 10: not in sync: resp=0xe2 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 8 of 10: not in sync: resp=0xe2 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 9 of 10: not in sync: resp=0xe2 avrdude: stk500_recv(): programmer is not responding avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0xe2 avrdude done. Thank you. Problem z wgrywaniem na płytkę. Program który wgrywam jest następujący : int data; void setup() { // put your setup code here, to run once: Serial.begin(115200); } void loop() { // put your main code here, to run repeatedly: data=Serial.read(); Serial.println(data); delay(200); } Jeśli odepnę wejścia TX, RX program działa normalnie i nie żadnego problemu. Po podłączeniu modułu tylko do zasilania i uruchomieniu terminala na telefonie po wpisaniu polecenie AT dostaje jakieś znaczki których nie można przeczytać. Używam Arduino Uno. Z góry przepraszam jeśli coś namieszałem jestem początkującym i to mój pierwszy problem którego nie mogę przejść. Proszę o pomoc
  3. Cześć wszystkim. Postanowiłem zbudować grawerkę laserową z instrukcji ze strony instructables. Zakupiłem wszystkie części zgodnie z opisem. Zmontowałem wszystkie mechaniczne części oraz ramę, pozostaje więc już jedynie podłączenie elektroniki. Schemat podany przez autora wygląda następująco: I tu pojawia się pierwszy problem, mianowicie sterowniki silników krokowych na schemacie są inne od sterowników użytych przez autora (oraz podanych w liście części). Te które występują na schemacie to najprawdopodobniej Easy Driver: Natomiast w liście części występuje sterownik TB6560 3A: Problem polega więc na tym, że nazwy wejść różnią się na obu sterownikach i nie wiem w jaki sposób podłączyć sterownik TB6560 mając informacje o podłączeniu sterownika Easy Driver (ze schematu). Przeczytałem kurs elektroniki z forum oraz przejrzałem ten dotyczący programowania Arduino lecz nie znalazłem informacji, które pozwoliłyby rozwikłać mi ten problem. Innych wątków dotyczących tego zagadnienia także nie znalazłem, a forum zostało mi polecone na uczelni (studia niezwiązane z elektroniką). Byłbym wdzięczny za wskazanie dalszej drogi. Nie mam doświadczenia z elektroniką w związku z czym chciałbym jeszcze spytać w jaki sposób można zaopatrzyć się w przewody? W internecie znalazłem jedynie przewody męskie, żeńskie itp. Czy jeśli mam na przykład podłączyć elementy odległe o 80cm to mam połączyć kilka takich przewodów, czy też lepiej kupić na długość przewód w sklepie elektrycznym? Dzięki za przeczytanie i pomoc
  4. Witam, Zainspirował mnie pomysł ze strony: https://create.arduino.cc/projecthub/kksjunior/windows-pc-lock-unlock-using-rfid-5021a6?ref=tag&ref_id=rfid&offset=0 aczkolwiek zmieniłbym parę rzeczy. Części do projektu: -Arduino Pro Micro -Moduł RFID RC522 -Dioda LED zielona -Dioda LED czerwona -2 Rezystory 220Ohm -Rezystor 67Ohm -Rezystor 270Ohm -Buzzer (opcjonalne) -Przełącznik na 3 piny (opcjonalne) Najpierw zaprojektowałem sam układ, który wygląda o tak: Gdy wszystko było gotowe zabrałem się do podłączania do breadboard'a testującego, ponieważ zawsze dobrze jest sprawdzić obwód zanim się go przylutuje na stałe. Chciałem jednak wziąć ten projekt o krok dalej, i zaprojektowałem płytkę PCB, i zamówiłem ją na https://jlcpcb.com : Pojawił się jednak mały problem, ponieważ mam tylko arduino nano i uno, ale potrzeba mikroprocesora atmega32u4, który można znaleźć tylko w arduino MEGA i pro Micro. Wybrałem Pro Micro, ponieważ chcę, aby ten obwód był jak najmniejszy. Po podłączeniu wszystkiego, działało to bez problemu. Dla zainteresowanych, oto kod: pc.rar Teraz tylko czekam, aż moja PCB przyjedzie.
  5. Proponuję rozpocząć kolejną "luźną" dyskusję. Arduino i Raspberry Pi zdecydowanie zrewolucjonizowały elektronikę w kontekście hobbystów i twórców, ale oczywiście znalazły one również swoje miejsce przy bardziej profesjonalnych zastosowaniach. Następnym przełomem, który się wydarzył (a właściwie trwa teraz) była popularyzacja ESP, czyli taniego, niezwykle wydajnego układu, który pozwala tworzyć urządzenia IoT. Moje pytanie brzmi więc następująco: jakie cechy Waszym zdaniem, będzie miała kolejna platforma, która przebojem wejdzie na rynek? Czego według Was brakuje? Większej wydajności, energooszczędności, a może jeszcze niższej ceny? Jak wiadomo przełomem będą np. komputery kwantowe, ale nie o takie rzeczy mi teraz chodzi. Pytam o realne (na dzisiejsze czasy) platformy, które mogłyby łatwo zagościć w warsztacie każdego majsterkowicza. Czego brakuje Wam w aktualnie popularnych platformach?
  6. Cześć, jestem początkujący i mam trzy problemy z którymi nie mogę się uporać. 1. Obracający się serwomechanizm Zakupiłem serwo "Tower Pro MicroServo 99", działa one jedynie w przedziale 10-180 stopni (mimo wpisanego kąta nie obraca się o kąt w pełni oczekiwany). Ponadto dla wpisanych wartości obrotu 0-10 serwo nieustannie się obraca. Podobno występują dwa rodzaje serwomechanizmów i prawdopodobnie zamówiłem złe, czy jest to prawdą, czy może moje jest popsute? 2. Błędne działanie kodu Stworzyłem program taki jak na poniższym przykładzie. Chcę tutaj wpisywać z komputera wartości obrotu do serwa oraz otrzymywać komunikat zwrotny o wpisanej wartości. Niestety po każdej wpisanej wartości następna automatyczna wartość to 0. Gdzie popełniłem błąd. #include <Servo.h> Servo myservo; void setup() { myservo.attach(9); Serial.begin(9600); Serial.setTimeout(100); myservo.write(180); } void loop() { static int odebraneDane; if(Serial.available() > 0) { odebraneDane = Serial.parseInt(); Serial.println(odebraneDane); if(odebraneDane > 1 && odebraneDane < 181){ myservo.write(odebraneDane); } else{ Serial.println("Wpisales zly kat mogles spalic servo(wpisz kat od 0-180 stopni): "); } delay(50); } } Otrzymane wyniki: 70 0 Wpisales zly kat mogles spalic servo(wpisz kat od 0-180 stopni): 90 0 Wpisales zly kat mogles spalic servo(wpisz kat od 0-180 stopni): 180 0 Wpisales zly kat mogles spalic servo(wpisz kat od 0-180 stopni): 3. Chciałbym kupić krańcówki do arduino. Na botlandzie znalazłem takie: https://botland.com.pl/pl/czujniki-krancowe/921-wylacznik-czujnik-krancowy-z-dzwignia-prosta-wk315.html Czy będą one kompatybilne? Rozumiem, że na jeden koniec podam 5V na drugi GND, a środkowy pin będzie informował stanem wysokim/niskim o naciśnięciu krańcówki? Z góry dzięki za każdą pomoc.
  7. Witam, Piszę do was szanowni koledzy gdyż potrzebuję pomocy a zarazem też trochę wyjaśnienia dlaczego tak a nie inaczej się dzieje, przejdę zatem do sedna. Mam płytkę Arduino Mega klon + ethernet shield + moduł lcd z guzikami i do tego jest jeszcze podłączone dwa moduły 8 kanałowych przekaźników. W sytuacji kiedy odpalam cały ten zestaw i ustawienia przekaźników maja stan w którym zapalają się wszystkie diody na modułach wyświetlacz zaczyna mocno przygasać i w momencie kiedy ethernet shield uzyska połączenie z routerem całość po prostu się resetuje, postanowiłem więc podłączyć zasilanie poprzez gniazdo zasilające i na zasilaczu zacząłem regulować napięcie, kiedy osiągnąłem około 8V cały zestaw pracuje ale reakcje wyświetlacza są zdecydowanie opóźnione i można powiedzieć, że kontrast jest znacząco słaby ale jak do tego podłącze USB zaczyna to jakoś w miarę rozsądnie pracować. Natomiast jak mam podłączony jeden moduł przekaźników to wszystko śmiga ładnie pięknie nawet na 5v z portu USB w komputerze. Dodam, że bez znaczenia który z modułów przekaźników używam, jak jest jeden to jest ok. Obydwa te moduły przekaźnikowe zasilanie mają podłączone w sposób równoległy z arduino. Proszę o pomoc jak mam to podłączyć lub jakie zasilanie dać, żeby wyświetlacz i reszta działały jak należy bo mnie już brakło pomysłów.
  8. Urządzenie MIDI na Arduino Pro Micro Czas na zrobienie fajnego urządzenia. W tym artykule postaram się zadowolić ludzi związanych z tworzeniem muzyki. Stwórzmy mikser Midi i podłączymy go do programu dla DJ-ów (Traktor Pro 2). Urządzenie będzie miało 8 obrotowych potencjometrów, które pozwalają kontrolować głośność, niskie, średnie i wysokie częstotliwości korektora oraz 2 przyciski odtwarzania / pauzy. Komponenty Podstawą naszego projektu będzie płyta arduino pro micro (lub arduino leonardo). Będziemy kontrolować potencjometry obrotowe. Na nich nakładamy kolorowe czapki. Będziesz potrzebował dwóch przycisków i dużego kondensatora przy 4700 UF (od 6V). Wpichnijmy to wszystko do pudełka. Elektroniczny obwód i montaż. Najpierw przygotujmy pudełko. Konieczne jest przecięcie 8 otworów o średnicy 6 milimetrów, po dwa otwory po 12 milimetrów i otwór na tylną ściankę. Kolejny etap to montowanie potencjometrów i przycisków. Włóż część i dokręć nakrętkę. Nadszedł czas na lutowanie komponentów razem. Zrobiłem wszystko na zielonej płycie prototypowej. Możesz to zrobić na bredboardzie lub wytrawić / zamówić płytkę. Nie ignoruj kondensatora, bez niego urządzenie nie zadziała! Szkic Algorytm programu jest prosty. Przeszukujemy potencjometry i przyciski, wysyłamy dane do komputera. #include <frequencyToNote.h> #include <MIDIUSB.h> #include <pitchToFrequency.h> #include <pitchToNote.h> #define n_pots 8 int val; int last_val[n_pots]={0,0,0,0,0,0,0,0}; int pot_pins[n_pots]={0,1,2,3,6,7,8,9}; #define btn0pin 2 #define btn1pin 3 void setup() { Serial.begin(9600); pinMode(btn0pin, INPUT); pinMode(btn1pin, INPUT); } void loop() { for (int i =0; i<n_pots;i++){ val = (int)analogRead(pot_pins[i])/8; if (val != last_val[i]) { last_val[i] = val; controlChange(0, i, val); } } if (digitalRead(btn0pin)){ controlChange(0,9,1); while(digitalRead(btn0pin)){} } if (digitalRead(btn1pin)){ controlChange(0,10,1); while(digitalRead(btn1pin)){} } delay(30); } void controlChange(byte channel, byte control, byte value) { midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value}; MidiUSB.sendMIDI(event); MidiUSB.flush(); } Można zrobic 3d obudowę do miksera. Tak wygląda moja , ale jej plik źródłowy tu nie zostawię, bo jest dużo błędów. Kształt jest fajny, ale techniczna strona nie. Użycie: dla przykładu użycia wziąłem traktor pro 2. Otwórz go, a następnie znajdź w nim "controller manager". Tutaj wiążemy potencjometry z dźwigniami interfejsu. Naciśnij “Add device” - “Generic MIDI”. Wybierz wejscie - “Arduino Leonardo”. Teraz musisz określić wszystkie elementy sterujące. Istnieją dwa kanały, z których każdy ma: głośność, wysokie, średnie i niskie częstotliwości oraz przycisk "Play/Pause". Kliknij "Add in" i znajdź Volume / High / Mid / Low. Wybierz kanał (A lub B). Kliknij “learn”. Przekręć potencjometr. Kliknij “learn”. Zrób to z wszystkimi elementami. Dodajemy przycisk “Play/Pause”. Dodajcie go, analogicznie potencjometru. Zmienimy tryb pracy przyciska do “Toggle”. I filmik demonstracja (niestety słowa rosyjskie, ale pracę można zobaczyć): Będę czekał na komentarze!
  9. Mamy budynek gospodarczy oddalony od domu o kilka(naście) metrów i chcemy upewnić się, że panują tam warunki odpowiednie dla zwierząt. Jak do tej pory nasze kury nie były zainteresowane dostępem do Internetu, nie mamy więc tam zasięgu sieci WiFi. Musimy więc jakoś "podkablować radiowo" dane i zaprezentować je w naszym pokoju. Założeniami projektu, o który prosił mnie kolega było: bezprzewodowe transmitowanie danych z czujnika do domu, przy czym sam czujnik "nie ma dostępu do Internetu" wyświetlanie aktualnego odczytu na LCD oraz aktywacja alarmu w razie spadku temperatury poniżej zadanej wartości zapisywanie wyników na stronie WWW dostępnej poprzez Internet, dzięki czemu warunki można sprawdzać będąc poza domem Czujnik z nadajnikiem Nadajnik zasilany jest "kradnąc" prąd ze stale włączonego zasilacza instalacji alarmowej. Dlatego też konieczne było zastosowanie przetwornicy zmniejszającej napięcie do 5V. Składniki: Arduino Pro Mini 328 - 5V/16MHz termometr BMP280 moduł nadajnika RF433MHz przetwornica step-down LM2596 Pamiętajmy, że BMP280 toleruje napięcie 3.3V na linii danych, a nasze Arduino wykorzystuje 5V. Najlepiej więc podłączyć szynę I2C poprzez dzielnik napięcia lub konwerter poziomów logicznych. Ja wstawiłem szeregowo rezystor 4.7kΩ na obu pinach I2C, co nie jest rozwiązaniem "profesjonalnym" i zalecanym, ale (chwilowo) działa #include <RCSwitch.h> //https://github.com/sui77/rc-switch #include <Wire.h> #include <SPI.h> #include <Adafruit_BMP280.h> Adafruit_BMP280 bmp; RCSwitch mySwitch = RCSwitch(); void setup() { Serial.begin(9600); if (!bmp.begin(0x76)) //changed from default I2C adress 0x77 { Serial.println("Nie odnaleziono czujnika BMP085 / BMP180"); while (1) { } } mySwitch.enableTransmit(10); } void loop() { int temperature = (int)bmp.readTemperature(); mySwitch.send(temperature, 24); delay(10000); } Odbiornik ESP8266 odbiera dane z modułu RF433MHz i regularnie przesyła je na stronę WWW. Stanowi ją skrypt PHP, który zapisuje do bazy danych oraz prezentuje wyniki. Wystarczy, by ESP pobrało adres skryptu i metodą GET przekazało dane (http://mojadomena.pl/skryptodbiorczy.php?haslo=tajnehaslo&temperatura=21). Składniki: ESP8266 moduł odbiornika RF433MHz wyświetlacz LCD 2x16 z konwerterem I2C LCM1602 buzzer Najtańsze moduły RF433 oferują jedynie kilkadziesiąt cm zasięgu. Musimy wykonać własną antenę z kawałka drutu, by uzyskać użyteczną komunikację. #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <ESP8266HTTPClient.h> #include <RCSwitch.h> //https://github.com/sui77/rc-switch #include <Wire.h> #include <LiquidCrystal_I2C.h> //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library #define MinimumTemperature 16 #define TempAlarmPin D1 LiquidCrystal_I2C lcd(0x27, 16, 2); HTTPClient httpc; const char* ssid = ""; const char* password = ""; const String accesspassword = "nojakieshaslo"; const String scripturl = "http://domena.saf/receivedata.php"; const char* WiFiHostname = "kurnik"; unsigned long lastTimeLCD = 0; int temperature = 0; RCSwitch mySwitch = RCSwitch(); void setup() { pinMode(TempAlarmPin, OUTPUT); digitalWrite(TempAlarmPin, LOW); Serial.begin(9600); Wire.begin(D3, D4); Wire.setClock(100000); lcd.begin(); lcd.backlight(); mySwitch.enableReceive(4); // D2 pinMode(13, OUTPUT); WiFi.hostname(WiFiHostname); WiFi.softAPdisconnect(); WiFi.disconnect(); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); } void loop() { if (mySwitch.available()) { temperature = mySwitch.getReceivedValue(); Serial.print("Received "); Serial.println(temperature); mySwitch.resetAvailable(); if (temperature <= MinimumTemperature) { digitalWrite(TempAlarmPin, HIGH); } else { digitalWrite(TempAlarmPin, LOW); } } if (lastTimeLCD == 0 || millis() - lastTimeLCD >= 60000) { lastTimeLCD = millis(); lcd.clear(); lcd.print(String(temperature) + "C"); String url = scripturl + "?password=" + accesspassword + "&temperature=" + temperature; httpc.begin(url); int httpCode = httpc.GET(); //Send the request if (httpCode > 0) { //Check the returning code String payload = httpc.getString(); //Get the request response payload Serial.println(payload); } } } Skrypt PHP: <?php DEFINE ('DB_USER', ''); DEFINE ('DB_PASSWORD',''); DEFINE ('DB_HOST','localhost'); DEFINE ('DB_NAME',''); DEFINE ('DB_TABLE_NAME','zagroda'); DEFINE ('PasswordToSave','nojakieshaslo'); $variables = array( array("temperature", "float"), ); for($i = 0; $i < count($variables); $i++) { if($_REQUEST[$variables[$i][0]] != "") { $savetodatabase = 1; break; } } try{ $pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';port=3306', DB_USER, DB_PASSWORD); //echo 'Connected to database'; }catch(PDOException $e){ echo 'Cannot connect to database<br />'; } $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); if($savetodatabase && $_REQUEST["password"] == PasswordToSave) { if(!tableExists($pdo, DB_TABLE_NAME)){ $query= "CREATE TABLE ".DB_TABLE_NAME." (ID int NOT NULL AUTO_INCREMENT, "; for($i = 0; $i < count($variables); $i++) { $query= $query.$variables[$i][0]." ".$variables[$i][1].", "; } $query= $query."update_time BIGINT, PRIMARY KEY (ID))"; $pdo->exec($query); //echo '<br><br>'.$query.'<br><br>'; } $query="INSERT IGNORE INTO ".DB_TABLE_NAME." (ID,"; if($_REQUEST["temperature"] != ""){ $query = $query."temperature,update_time"; } $query= $query.") VALUES (1,"; if($_REQUEST["temperature"] != ""){ $query = $query."'".mysql_real_escape_string($_REQUEST["temperature"])."',"; } $query= $query."'".time()."') ON DUPLICATE KEY UPDATE "; if($_REQUEST["temperature"] != ""){ $query = $query."temperature=VALUES(temperature),update_time=VALUES(update_time)"; } //echo '<br><br>'.$query.'<br><br>'; $pdo->exec($query); } else { if($_REQUEST["values"] == "1"){ $query="SELECT * FROM `".DB_TABLE_NAME."` ORDER BY `ID` DESC LIMIT 10"; $stmt = $pdo -> query($query); $row = $stmt->fetch(/* PDO::FETCH_ASSOC */); echo $row[1].';'.$row[2].';'; $stmt->closeCursor(); } else{ echo '<html><head><meta charset="UTF-8"><title>Chicken coop</title></head> <body><table border="3"> <tr><td><b>Temperature</b></td> <td><b>Time</b></td></tr> <tr><td><span id="temp"></span>°C</td> <td><span id="time"></span></td></tr> </table> <script>myTimer();var myVar = setInterval(myTimer, 3000);function myTimer() { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(";"); document.getElementById("temp").innerHTML = values[0]; document.getElementById("time").innerHTML = Unix_timestamp(values[1]); } }; xhttp.open("GET", "receivedata.php?values=1", true); xhttp.send();} function Unix_timestamp(t) { var dt = new Date(t*1000); var day = dt.getDate(); var month = "0" + (dt.getMonth()+1); var year = dt.getFullYear(); var hr = dt.getHours(); var m = "0" + dt.getMinutes(); var s = "0" + dt.getSeconds(); return day+ "." + month.substr(-2) + "." + year + " " + hr+ ":" + m.substr(-2) + ":" + s.substr(-2); } </script> </body></html>'; } } function tableExists($pdo, $table) { try { $result = $pdo->query("SELECT 1 FROM $table LIMIT 1"); } catch (Exception $e) { return FALSE; } return $result !== FALSE; } ?> Jeżeli zechcemy dopisać do tego zapisywanie historii (przy obecnych założeniach projektu prezentowany jest jedynie najnowszy odczyt) i wykres zmian w czasie, polecam zapoznać się z biblioteką Google Charts. Przykład jej wykorzystania razem z AJAXem. Możemy również wykonać kolejny wyświetlacz np do innego pokoju czy nawet innego budynku, który odczytywał będzie wartości ze wspomnianej strony WWW. Wykorzystałem do tego znane nam ESP oraz wyświetlacz siedmiosegmentowy sterowany chipem MAX7219. #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266HTTPClient.h> #include <ESP8266WebServer.h> #include <EEPROM.h> #include "LedControl.h" //http://github.com/wayoda/LedControl #include <MD5Builder.h> #define WiFimodeButton 14 //D5 String scripturl = ""; String ssid = "", pass = ""; String host, url, http; const int httpPort = 80; const char* WiFiHostname = "Henhouse"; const String accesspassword = "nojakieshaslo"; unsigned long lastTimeLCD = 0, lastDataRead; unsigned int intervalDataRead = 5; WiFiClient client; ESP8266WebServer server(80); HTTPClient httpc; // EasyESP or NodeMCU Pin D8 to DIN, D7 to Clk, D6 to LOAD, no.of devices is 1 LedControl lc = LedControl(D8, D7, D6, 1); void setup() { pinMode(WiFimodeButton, INPUT_PULLUP); Serial.begin(9600); /* The MAX72XX is in power-saving mode on startup, we have to do a wakeup call */ lc.shutdown(0, false); /* Set the brightness to a medium values */ lc.setIntensity(0, 1); /* and clear the display */ lc.clearDisplay(0); EEPROM.begin(512); EEPROM.get(0, intervalDataRead); WiFi.softAPdisconnect(); WiFi.disconnect(); int i; if (ssid == "") { for (i = 100; i < 170; i++) { if (EEPROM.read(i) == NULL) { break; } ssid += char(EEPROM.read(i)); } for (i = i + 1; i < 250; i++) { if (EEPROM.read(i) == NULL) { break; } pass += char(EEPROM.read(i)); } } if (scripturl == "") { for (i = i + 1; i < 450; i++) { if (EEPROM.read(i) == NULL) { break; } scripturl += char(EEPROM.read(i)); } } if (digitalRead(WiFimodeButton) == LOW) { //access point part Serial.println("Creating Accesspoint"); IPAddress apIP(192, 168, 1, 1); WiFi.mode(WIFI_AP); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); WiFi.softAP("aquarium", "aquarium123"); Serial.print("IP address:\t"); Serial.println(WiFi.softAPIP()); } else { //station part Serial.print("connecting to..."); Serial.println(ssid); WiFi.hostname(WiFiHostname); WiFi.mode(WIFI_STA); WiFi.begin(ssid.c_str(), pass.c_str()); /* while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } */ Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } server.on("/", handleRoot); server.on("/login", handleLogin); const char * headerkeys[] = {"Cookie"} ; size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*); //ask server to track these headers server.collectHeaders(headerkeys, headerkeyssize); server.begin(); } void loop() { if (lastDataRead == 0 || (millis() - lastDataRead >= intervalDataRead * 1000)) { //host = scripturl.substring(0, scripturl.indexOf('/')); httpc.begin("http://" + scripturl + "?values=1"); //Specify request destination int httpCode = httpc.GET(); //Send the request if (httpCode > 0) { //Check the returning code String payload = httpc.getString(); //Get the request response payload Serial.println(payload); //Print the response payload String temperature1 = explode(';', payload, 0); //String temperature2 = explode(';', payload, 2); Serial.print(temperature1); //Serial.print(" "); //Serial.println(temperature2); lc.clearDisplay(0); auto printLED = [](unsigned char beginLEDsegment, String printString)->void { int i; for (i = 0; i < printString.length(); i++) { lc.setChar(0, beginLEDsegment - i, (printString[i] == '.') ? printString[i + 1] : printString[i], (printString[i + 1] == '.') ? true : false); if (printString[i] == '.') { break; } } //lc.setChar(0, beginLEDsegment - i - 1, 'C', false); lc.setRow(0, beginLEDsegment - i - 1, B01001110); //"C" }; printLED(7, String(temperature1)); //printLED(3, String(temperature2)); if (payload.length() > 4) { lastDataRead = millis(); } else { lastDataRead += 1000; } } httpc.end(); } server.handleClient(); } void handleRoot() { if (!is_authentified()) { server.sendHeader("Location", "/login"); server.sendHeader("Cache-Control", "no-cache"); server.send(301); return; } int i; if (server.hasArg("SSID") && server.arg("SSID") != "" && server.hasArg("wifipass") && server.arg("wifipass") != "") { ssid = server.arg("SSID"); pass = server.arg("wifipass"); for (i = 0; i < server.arg("SSID").length(); i++) { EEPROM.write(i + 100, server.arg("SSID")[i]); } i += 100; EEPROM.write(i, NULL); for (i = 0; i < server.arg("wifipass").length(); i++) { EEPROM.write(i + 101 + server.arg("SSID").length(), server.arg("wifipass")[i]); } //i += 3 + server.arg("wifipass").length(); EEPROM.write(server.arg("SSID").length() + server.arg("wifipass").length() + 101, NULL); EEPROM.commit(); } if (server.hasArg("scripturl") && server.arg("scripturl") != "") { scripturl = server.arg("scripturl"); for (i = 0; i < scripturl.length(); i++) { EEPROM.write(i + 102 + ssid.length() + pass.length(), scripturl[i]); } EEPROM.write(ssid.length() + pass.length() + scripturl.length() + 102, NULL); EEPROM.commit(); } if (server.arg("intervalDataRead").toInt() > 0) { intervalDataRead = server.arg("intervalDataRead").toInt(); EEPROM.put(0, intervalDataRead); EEPROM.commit(); } if (server.hasArg("SSID") && server.arg("SSID") != "" && server.hasArg("wifipass") && server.arg("wifipass") != "") { ESP.restart(); } String content = "<html><head><title>Henhouse</title></head><body>"; content += "<form action='/' method='POST'>Home WiFi Network Name (SSID): <input type='text' name='SSID' value='" + String(ssid) + "'><br>"; content += "Password: <input type='password' name='wifipass'><br>"; content += "URL to datalogging script (without http://): <input type='text' name='scripturl' id='scripturl' value='" + scripturl + "'><br>"; content += "Read values every [seconds]: <input type='text' name='intervalDataRead' value='" + String((int)intervalDataRead) + "'><br>"; content += "<script language=\"javascript\">function stringReplace() {var s = document.getElementById(\"scripturl\").value;var removeThis = /^(http?|https):\\/\\//;s = s.replace(removeThis, '');document.getElementById(\"scripturl\").value = s;}</script>"; content += "<input type='submit' value='Submit' onClick=\"stringReplace();\"></form><br>"; content += "</body></html>"; server.send(200, "text/html", content); } bool is_authentified() { Serial.println("Enter is_authentified"); if (server.hasHeader("Cookie")) { Serial.print("Found cookie: "); String cookie = server.header("Cookie"); Serial.println(cookie); String cookielogin = "WEATHERSTATION=" + md5(accesspassword); if (cookie.indexOf(cookielogin) != -1) { Serial.println("Authentification Successful"); return true; } } Serial.println("Authentification Failed"); return false; } void handleLogin() { String msg; if (server.hasHeader("Cookie")) { Serial.print("Found cookie: "); String cookie = server.header("Cookie"); Serial.println(cookie); } if (server.hasArg("DISCONNECT")) { Serial.println("Disconnection"); server.sendHeader("Location", "/login"); server.sendHeader("Cache-Control", "no-cache"); server.sendHeader("Set-Cookie", "WEATHERSTATION=0"); server.send(301); return; } if (server.hasArg("PASSWORD")) { if (server.arg("PASSWORD") == accesspassword) { server.sendHeader("Location", "/"); server.sendHeader("Cache-Control", "no-cache"); String cookielogin = "WEATHERSTATION=" + md5(accesspassword); server.sendHeader("Set-Cookie", cookielogin); server.send(301); Serial.println("Log in Successful"); return; } msg = "Wrong username/password! try again."; Serial.println("Log in Failed"); } String content = "<html><body><form action='/login' method='POST'>"; content += "Password:<input type='password' name='PASSWORD' placeholder='password'><br>"; content += "<input type='submit' name='SUBMIT' value='Submit'></form>" + msg + "<br>"; server.send(200, "text/html", content); } String md5(String str) { MD5Builder _md5; _md5.begin(); _md5.add(String(str)); _md5.calculate(); return _md5.toString(); } String explode(char character, String string, unsigned int position) { unsigned int i, ii; unsigned int counter = 0; for (i = 0; i < string.length(); ++i) { if (string[i] == character) { counter++; i++; } if (counter >= position) { break; } } for (ii = i + 1; ii < string.length(); ii++) { if (string[ii] == character) { break; } } return string.substring(i, ii); }
  10. Witam! Chciałbym się pochwalić moim pierwszym stworzonym projektem, a mianowicie systemem monitorowania środowiska. Interesuję się IoT i jego obszarami działania. Stworzyłem system, który składa się z urządzenia pomiarowego, chmury internetowej oraz aplikacji mobilnej. Struktura systemu została podzielona na 3 etapy, odpowiednia: warstwa sprzętowa, chmury i aplikacji. Warstwa sprzętowa Urządzenie ma za zadanie pobieranie danych z otoczenia oraz pobieranie ich do zewnętrznego serwera. Badane czynniki to: temperatura, wilgotność, opady atmosferyczne i ruch. Do wykonania urządzenia wykorzystano poniższe podzespoły: Arduino Uno Rev3, Czujnik temperatry i wilgotności DHT11, Czujnik ruchu PIR HC-SR501, Czujnik opadów deszczu YL-83, Moduł sieciowy Ethernet ENC28J60 mini, przewody połączeniowe, płytkę stykową. Projekt urządzenia wygląda następująco: Oraz urządzenie wykonane zgodnie z projektem: Następnie funkcje sensorowe urządzenia zostały przetestowane. Na płytkę został wgrany poniższy kod: #include <dht11.h> #define CzujnikTempWilgPin 2 //definicja numerów pinów #define CzujnikOpadAnalogPin A0 #define CzujnikOpadDigitalPin 4 #define CzujnikRuchPin 8 int wartosc_A0; int wartosc_D0; int wilgotnosc; int temperatura; int ruch; dht11 DHT11; int interwal = 30000; void setup() { Serial.begin(9600); //inicjalizacja monitora szeregowego pinMode(CzujnikOpadDigitalPin, INPUT);//konfiguracja zachowania pinów pinMode(CzujnikRuchPin, INPUT); } void loop() { DHT11.read(CzujnikTempWilgPin); //odczyt danych z czujnika DHT11 wilgotnosc = DHT11.humidity; Serial.print("Wilgotnosc (%): "); Serial.print((float)wilgotnosc, 0); temperatura = DHT11.temperature; Serial.print(" Temperatura (C): "); Serial.println((float)temperatura, 0); ruch = digitalRead(CzujnikRuchPin); //odczyt danych z czujnika ruchu if(ruch == HIGH){ Serial.println("Wykryto ruch"); } else { Serial.println("Nie wykryto ruchu"); } wartosc_A0 = analogRead(CzujnikOpadAnalogPin); wartosc_D0 = digitalRead(CzujnikOpadDigitalPin); if (wartosc_D0 == LOW) { Serial.print("DESZCZ PADA Z INTENSYWNOŚCIĄ "); Serial.println(wartosc_A0); } else { Serial.println("DESZCZ NIE PADA"); } delay(interwal); } Test monitorowania parametrów: Konieczna była regulacja czujników, aby mierzone wartości były jak najbardziej dokładne. Warstwa chmury Jako chmurę przechowującą i analizującą dane wybrano serwis Thingspeak.com. Jako sposób komunikacji wybrano interfejs REST API. W pierwszej kolejności założono konto oraz publiczny kanał wyposażony w 5 pól: temperatura, wilgotność, ruch, opady atmosferyczne i intensywność opadów. Sprawdzono również klucze danych odpowiedzialne za zapis i odczyt danych. Poniższa grafika przedstawia przesłane dane z urządzenia pomiarowego. Warstwa aplikacji Końcowym elementem systemu jest urządzenie z systemem Android. Stworzono aplikację, dzięki której możliwe jest połączenie z kanałem na thingspeak.com oraz pobranie danych. Aplikacja została zaopatrzona we wszystkie wyżej wymienione wykresy oprócz wykresu intensywności opadów. Projekt aplikacji został przedstawiony powyżej. Wszystkie wykresy zostały umieszczone w panelu przesuwnym. Do stworzenia wykresów wykorzystano biblioteki graficzne na system android: biblioteka do tworzenia kanału thingspeak.com oraz bibliotekę do tworzenia wykresów. Cały kod stworzonej aplikacji na system android. Agregacja całego systemu Następnie przyłączono urządzenie do sieci oraz został wgrany poniższy kod: #include <dht11.h> #include <UIPEthernet.h> #define CzujnikTempWilgPin 2 #define CzujnikOpadAnalogPin A0 #define CzujnikOpadDigitalPin 4 #define CzujnikRuchPin 8 byte mac[] = { 0x54, 0x34, 0x41, 0x30, 0x30, 0x31 };//adres MAC urządzenia EthernetClient client; char server[] = "api.thingspeak.com";//adres api thingspeak int wartosc_A0; int wartosc_D0; int wilgotnosc; int temperatura; int ruch; int interwal = 30000; dht11 DHT11; void setup() { Ethernet.begin(mac); //rozpoczęcie połączenia pinMode(CzujnikOpadDigitalPin, INPUT); pinMode(CzujnikRuchPin, INPUT); } void loop() { DHT11.read(CzujnikTempWilgPin); wilgotnosc = DHT11.humidity; temperatura = DHT11.temperature; ruch = digitalRead(CzujnikRuchPin); wartosc_A0 = analogRead(CzujnikOpadAnalogPin); wartosc_D0 = digitalRead(CzujnikOpadDigitalPin); if (client.connect(server, 80)) {//składanie komendy get client.print("GET /update?"); client.print("key=POZ0YSHN2VGMKS05"); client.print("&field1="); client.print(temperatura); client.print("&field2="); client.print(wilgotnosc); client.print("&field3="); client.print(wartosc_D0); if(wartosc_D0==LOW) { client.print("&field4="); client.print(wartosc_A0); } client.print("&field5="); client.print(ruch); client.println( " HTTP/1.1"); client.print( "Host: " ); client.println(server); client.println( "Connection: close" ); client.println(); client.println(); client.stop();// } delay(interwal); } System funkcjonował prawidłowo dane były przesyłane na serwer. Następnie uruchomiono aplikację mobilną. Oto kilka screenów: Dodatkowymi funkcjami apikacji jest przesuwanie oraz przybliżanie wykresów oraz odczytywanie dokładnych wartości po kliknięciu w punkt wykresu. Mam nadzieję, że przedstawiłem to w ciekawy i przejrzysty sposób. :) Pozdrawiam
  11. Cześć!!! Podczas pisania kodu programu natrafiłem na problem. Pisze program w którym każdy solenoid (na razie dioda) odpowiada, numerowi. Na ekranie wyświetla się coś w stylu menu z liczbami, którym steruje się dwoma przyciskami (góra[1], dół[2]). Po wybraniu opcji zatwierdza się ją przyciskiem[3]. Problem polega na tym, że nie wiem jak zrobić aby program "wykrywał" pojedyncze liczby i zaświetlił odpowiednie diody. np. Liczba: 1 2 3 . 4 5 Diody: 4 5 6 2 7 8 Solenoidy będą naciskały klawiaturę numeryczną. Nie da się wpiąć bezpośrednio do urządzenia (Czyli do wielkich ekranów 7-dmio segmentowych, którymi steruje ww. klawiatura.). Kiedyś pisałem już temat na tym forum dotyczący tego samego programu: https://tiny.pl/txc4v Z góry dziękuję!!! #include <LCD.h> #include <LiquidCrystal.h> #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <EasyButton.h> #define czasZatw 200 #define czasGoraDol 200 #define czas000 5000 #define czasSolen 1000 #define czasNaci 500 #define BACKLIGHT_PIN 3 LiquidCrystal_I2C lcd(0x3F,2,1,0,4,5,6,7); EasyButton zatw(A0); EasyButton wdol(A1); EasyButton wgore(A2); int array[15] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14 }; String a[17]; short b=-1; byte d=0; byte c=0; byte g=1; byte h=2; byte i=3; byte j=4; byte k=5; byte l=6; byte m=7; byte n=8; byte o=9; void setup() { pinMode(2,OUTPUT); pinMode(3,OUTPUT); pinMode(4,OUTPUT); pinMode(5,OUTPUT); pinMode(6,OUTPUT); pinMode(7,OUTPUT); pinMode(8,OUTPUT); pinMode(9,OUTPUT); pinMode(10,OUTPUT); pinMode(11,OUTPUT); pinMode(12,OUTPUT); Serial.begin(115200); zatw.begin(); wdol.begin(); wgore.begin(); zatw.onPressed(ztawierdz); wdol.onPressed(dol); wgore.onPressed(gora); pinMode(13,OUTPUT); lcd.begin (16,2); lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE); lcd.setBacklight(HIGH); lcd.setCursor(0,0); lcd.print("Aktualny: "); lcd.print(a[b]); lcd.setCursor(0,1); lcd.print("Nastepny: "); lcd.print(a[b+1]); a[16]="------"; a[15]="------"; a[14]="15.30"; a[13]="14.03"; a[12]="13.01"; a[11]="12.35"; a[10]="11"; a[9]="10"; a[8]="9"; a[7]="8.44"; a[6]="7"; a[5]="6.26"; a[4]="5.04"; a[3]="4.03"; a[2]="3"; a[1]="751.3"; a[0]="0.45"; } void ztawierdz() { if(b<16&&b>=0) { lcd.clear(); Serial.println("zatwierdzono!!!"); b++; c++; lcd.setCursor(0,0); lcd.print("Aktualny: "); lcd.print(a[b]); lcd.setCursor(0,1); lcd.print("Nastepny: "); lcd.print(a[b+1]); b=c; digitalWrite(13, HIGH); delay(czasZatw); digitalWrite(13, LOW); delay(czasZatw); digitalWrite(13, HIGH); delay(czasZatw); digitalWrite(13, LOW); delay(czasZatw); digitalWrite(13, HIGH); delay(czasZatw); digitalWrite(13, LOW); digitalWrite(2,HIGH); delay(czasSolen); digitalWrite(2,LOW); delay(czasNaci); if(a[0][0]%g){ digitalWrite(3,HIGH); delay(czasSolen); digitalWrite(3,LOW); delay(czasNaci); } if(a[0][0]%h) { digitalWrite(4,HIGH); delay(czasSolen); digitalWrite(4,LOW); delay(czasNaci); } if(a[c][d]==h) { digitalWrite(5,HIGH); delay(czasSolen); digitalWrite(5,LOW); delay(czasNaci); } if(a[c][d]==i) { digitalWrite(6,HIGH); delay(czasSolen); digitalWrite(6,LOW); delay(czasNaci); } if(a[c][d]==j) { digitalWrite(7,HIGH); delay(czasSolen); digitalWrite(7,LOW); delay(czasNaci); } if(a[c][d]==k) { digitalWrite(8,HIGH); delay(czasSolen); digitalWrite(8,LOW); delay(czasNaci); } if(a[c][d]==l) { digitalWrite(9,HIGH); delay(czasSolen); digitalWrite(9,LOW); delay(czasNaci); } if(a[c][d]<m) { digitalWrite(10,HIGH); delay(czasSolen); digitalWrite(10,LOW); delay(czasNaci); } if(a[c][d]==n) { digitalWrite(11,HIGH); delay(czasSolen); digitalWrite(11,LOW); delay(czasNaci); } if (a[c][d]==o) { digitalWrite(12,HIGH); delay(czasSolen); digitalWrite(12,LOW); delay(czasNaci); } d+1; } } void dol() { if(b<16&&b>=1) { lcd.clear(); Serial.println("w dol!!!"); b--; lcd.setCursor(0,0); lcd.print("Aktualny: "); lcd.print(a[b]); lcd.setCursor(0,1); lcd.print("Nastepny: "); lcd.print(a[b+1]); digitalWrite(13, HIGH); delay(czasGoraDol); digitalWrite(13, LOW); } } void gora() { if(b<15) { lcd.clear(); Serial.println("w gore!!!"); b++; lcd.setCursor(0,0); lcd.print("Aktualny: "); lcd.print(a[b]); lcd.setCursor(0,1); lcd.print("Nastepny: "); lcd.print(a[b+1]); digitalWrite(13, HIGH); delay(czasGoraDol); digitalWrite(13, LOW); } } void loop() { zatw.read(); wdol.read(); wgore.read(); }
  12. Wstęp: Na wstępie chciałbym zaznaczyć, iż jest to mój pierwszy projekt, w którym miałem możliwość wykorzystać software do projektu własnej płytki PCB i skonstruować coś co nie bazuje na płytce Arduino i nie jest plątaniną przewodów na płytce stykowej. Proszę o wyrozumiałość, a zarazem o konstruktywną krytykę i cenne wskazówki, które mogą być przydatne przy tworzeniu kolejnych projektów. Wykonany prze ze mnie projekt nie jest innowacyjny, ale dzięki niemu mogłem spróbować swoich przede wszystkim w wytrawieniu pierwszej płytki PCB. Opis: Wynikiem końcowym projektu jest niewielkie pudełko/sejf do przechowywania drobnych rzeczy, które można otworzyć po wpisaniu hasła, bądź przyłożeniu karty. Sercem układu jest mikrokontroler, który można spotkać w Arduino UNO R3. Ze względu na prostotę środowiska ArduinoIDE zdecydowałem się na implementację kodu właśnie w tym programie. W związku z tym wybór padł na mikrokontroler ATmega328P z wgranym bootloaderem dla Adruino. Takie rozwiązanie pozwoliło mi na wykorzystanie gotowych bibliotek do obsługi modułu RFID i klawiatury numerycznej. Zdecydowałem się na brak wewnętrznego źródła zasilania. Został wyprowadzony przewód USB, dzięki któremu urządzenie można podpiąć pod dowolne wyjście USB (powerbank, port USB w komputerze, ładowarka sieciowa). Urządzenie jest więc zasilane napięciem 5V DC. Wykorzystane elementy: Na projekt składają się wymienione wcześniej elementy, tj.: ATmega328P moduł RFID klawiatura numeryczna 4x3 konwerter poziomów logicznych oraz: stabilizator napięcia LF33CV rezonator kwarcowy 16MHz serwomechanizm rezystory 22Ohm oraz 10kOhm kondensatory 100nF diody LED (czerwona i zielona) + oprawki przewód USB + odgiętka na przewód przewody połączeniowe drewniane pudełko (brak linku, elementy znaleziony w piwnicy w trakcie porządków) dystans nylonowy Budowa: Po przetestowaniu działania urządzenia na płytce prototypowej znalazłem odpowiednie pudełko, w którym zmieści się układ i będzie stosunkowo estetycznie wszystko rozmieszczone. Wyciąłem miejsce na klawiaturę, miejsce na blokadę serwomechanizmu oraz wywierciłem otwory na oprawki na diody. W kolejnym kroku pokryłem pudełko matową farbą i wnętrze wypełniłem materiałem zbliżonym do okleiny na głośnikach i tubach basowych. Po wykonaniu płytki przystąpiłem do lutowania elementów. Klawiatura, moduł RFID, diody, serwomechanizm oraz złącze zasilania zostały połączone z płytką poprzez przewody połączeniowe z końcówką żeńską. Płytka PCB została umieszczona w górnej części pudełka na nylonowych dystansach. Pod płytką znalazło się miejsce na moduł RFID oraz klawiaturę i diody. Na koniec wyciąłem odpowiedni fragment deseczki balsowej z miejscem na serwomechanizm, aby ukryć przewody połączeniowe. Deseczkę balsową okleiłem tym samym materiałem co wnętrze pudełka. Oprogramowanie: Kod programu został napisany w środowisku ArduinoIDE z wykorzystaniem następujących bibliotek: Servo.h Keypad.h Password.h SPI.h MFRC522.h Kod programu: #include <Servo.h> #include <Keypad.h> #include <Password.h> #include <SPI.h> #include <MFRC522.h> //Servo Servo servo; //Password Password password = Password("1234"); //RFID const byte UID1[] = {0x70, 0xC3, 0xF9, 0x65}; const byte UID2[] = {0x35, 0xDC, 0xD7, 0x65}; const byte ROWS = 4; const byte COLS = 3; char keys[ROWS][COLS] = { {'1', '2', '3'}, {'4', '5', '6'}, {'7', '8', '9'}, {'#', '0', '*'} }; byte rowPins[ROWS] = {4, 3, 2, 0}; byte colPins[COLS] = {8, 7, 6}; Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); MFRC522 rfid(10, 5); MFRC522::MIFARE_Key key; void setup() { Serial.begin(9600); SPI.begin(); rfid.PCD_Init(); pinMode(A0,OUTPUT); pinMode(A1,OUTPUT); servo.attach(9); servo.write(0); keypad.addEventListener(keypadEvent); digitalWrite(A0,HIGH); digitalWrite(A1,LOW); } void loop() { keypad.getKey(); if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) { if (rfid.uid.uidByte[0] == UID1[0] && rfid.uid.uidByte[1] == UID1[1] && rfid.uid.uidByte[2] == UID1[2] && rfid.uid.uidByte[3] == UID1[3]) { servo.write(90); digitalWrite(A0, LOW); digitalWrite(A1, HIGH); } else if (rfid.uid.uidByte[0] == UID2[0] && rfid.uid.uidByte[1] == UID2[1] && rfid.uid.uidByte[2] == UID2[2] && rfid.uid.uidByte[3] == UID2[3]) { servo.write(90); digitalWrite(A0, LOW); digitalWrite(A1, HIGH); } } } void keypadEvent(KeypadEvent eKey) { switch (keypad.getState()) { case PRESSED: Serial.println(eKey); switch (eKey) { case '#': checkPassword(); delay(1); break; case '*': closeBox(); delay(1); break; default: password.append(eKey); delay(1); } } } void checkPassword() { if (password.evaluate()) { servo.write(90); digitalWrite(A0, LOW); digitalWrite(A1, HIGH); } else { servo.write(0); digitalWrite(A0, HIGH); digitalWrite(A1, LOW); } } void closeBox() { password.reset(); servo.write(0); digitalWrite(A0, HIGH); digitalWrite(A1, LOW); } Działanie programu: Aby odblokować serwomechanizm i otworzyć pudełko należy wpisać odpowiedni kod zdefiniowany w programie i zatwierdzić znakiem "*" lub przyłożyć odpowiednią kartę (zdefiniowane są dwie karty). Po odblokowaniu gaśnie dioda czerwona, zapala się dioda czerwona i serwomechanizm zmienia położenie. Aby zamknąć pudełko należy wcisnąć przycisk "#". Podsumowanie: Projekt uważam za prawie zakończony. Po złożeniu całego urządzenia zwróciłem uwagę na brak możliwości zmiany kodu oraz dodania nowej karty (możliwe jedynie poprzez przeprogramowanie ATmegi). Projekt płytki zapewne pozostawia sporo do życzenia, dlatego liczę na cenne wskazówki aby nie popełniać błędów w przyszłości. Za wadę uważam też brak dodatkowej filtracji zasilania (stwierdziłem, że skoro biorę zasilanie z USB to nie trzeba nic dodawać). Zaletą (albo i wadą ) urządzenia są małe gabaryty. Ja wykorzystuje je do przechowywania zdjęć wykonanych Instaxem. Załączniki: W projekcie załączam kod programu, wykorzystane biblioteki i projekt Eagle do płytki PCB. Zdjęcia: safe_box_eagle.zip safe_box_software.zip
  13. Cześć W ramach organizowanej akcji postanowiłem opisać projekt, który wykonałem już jakiś czas temu. WPROWADZENIE Projekt to stacja meteorologiczna montowana na wyrzutni rakiet zaprojektowana i wykonana przeze mnie w ramach rekrutacji do Sekcji Rakietowej Studenckiego Koła Astronautycznego (działającego na Politechnice Warszawskiej). Wymagania wobec stacji: pomiar prędkości i kierunku wiatru, pomiar temperatury i ciśnienia, pomiar położenia oraz orientacji wyrzutni i kąta nachylenia ramienia, komunikacja bezprzewodowa z bazą, zasięg 500-700m, aplikacja na PC wyświetlająca i rejestrująca odbierane dane. ELEKTRONIKA Ogólny diagram sprzętowy wygląda następująco: Do realizacji 'mózgu' stacji wykorzystałem Arduino Pro Mini (zarówno w nadajniku, jak i odbiorniku). Komunikacja bezprzewodowa zrealizowana jest poprzez moduły NRF24L01+ ze wzmacniaczem mocy (w przeprowadzonych na poligonie testach bez problemu uzyskiwały zasięg 700m). Do pomiaru ciśnienia i temperatury zastosowałem moduł z układem BMP180. Pomiar prędkości i kierunku wiatru jest wykonywany przez zestaw stacji pogodowej. Do pomiaru kąta nachylenia wyrzutni zastosowałem moduł z układem MPU-6050. Położenie wyrzutni jest mierzone poprzez standardowy moduł GPS (NEO-7M-C firmy Waveshare). Urządzenie montowane na wyrzutni zasilane jest dwoma ogniwami litowo-jonowymi, a odbiornik jest zasilany przez komputer za pośrednictwem USB. Do generacji napięcia 5V w nadajniku wykorzystałem moduł przetwornicy D24V6F5 z Pololu. Z przeprowadzonych pomiarów wynika, że jedno ładowanie baterii pozwala działać stacji przez około 30 godzin. Schemat jednostki głównej: Ciekawą sprawą jest pomiar prędkości wiatru. Anemometr raz na obrót zwiera styk, a prędkość obrotowa jest proporcjonalna do prędkości wiatru (1Hz przekłada się na 2.4km/h). Mierząc więc okres pomiędzy kolejnymi krawędziami, można określić prędkość wiatru. Aby pomiar był możliwie dokładny, wykorzystałem przerwania. Jednak jak to z mechanicznymi stykami bywa, pojawiały się drgania styków: Aby zniwelować ten efekt zastosowałem dwie bramki NOT z przerzutnikami Schmitta i filtrem RC (o stałej czasowej około 1ms). Układ sprawia, że na pinie mikrokontrolera pojawiają się ładne, pojedyncze zbocza (czerwony to wyjście anemometru, a niebieski przebieg to sygnał podłączony do pinu mikrokontrolera): Na pokładzie zamontowany jest również magnetometr (znajdujący się poza główną obudową, w oddzielnej obudowie), który miał służyć do pomiaru azymutu wyrzutni (przy startach rakiet bardzo istotne są dwa kąty: azymut oraz nachylenie ramienia wyrzutni). Ostatecznie jednak nie został wykorzystany ze względu na problemy z kalibracją (magnetometr musiałby być za każdym razem montowany dokładnie tak samo na wyrzutni, co nie jest wykonalne). Odbiornik jest stosunkowo prostym urządzeniem: Zawiera tylko mikrokontroler (na module Arduino Pro Mini) oraz moduł radiowy. Komunikacja z PC wykonywana jest przez konwerter USB-UART. Odbiornik wyposażony jest też w diodę, która jest zapalana na chwilę w momencie odebrania pakietu. Bardzo to pomaga przy rozstawianiu stacji. Oba układy zmontowałem na płytkach prototypowych. OPROGRAMOWANIE Oprogramowanie projektu jest dość rozbudowane, gdyż składa się z dwóch projektów na systemy wbudowane (nadajnik i odbiornik) oraz oprogramowania na PC. Oprogramowanie nadajnika Kod napisany oczywiście z wykorzystaniem środowiska i bibliotek Arduino dla przyspieszenia developmentu Wykorzystałem dodatkowe biblioteki: MPU6050 (konfiguracja i odczytywanie danych z akcelerometru) HMC5883L (konfiguracja i odczytywanie danych z magnetometru) I2Cdev (wykorzystywane przez bibliotekę MPU6050) TinyGPS (parsowanie danych z GPS) RF24 (komunikacja z modułem radiowym) Adafruit_BMP085 (konfiguracja i odczytywanie danych z barometru) Oprogramowanie odbiornika Zadaniem programu uruchomionego na odbiorniku jest odczytanie danych z modułu radiowego i przerzucenie ich od razu na port szeregowy. Nic skomplikowanego. Kod na systemy wbudowane jest w repozytorium: https://github.com/Lukaszm94/rocketLauncherDAQ_embedded Oprogramowanie na PC Program do uruchamiany na PC służy do odbierania danych z odbiornika, wyświetlania ich i zapisywania do pliku CSV. Do zrealizowania powyższych zadań wykorzystałem język C++, framework Qt i bibliotekę Qwt. Kod znajduje się w repozytorium: https://github.com/Lukaszm94/RocketLauncherDataAcquisitionApp Na temat samej implementacji nie będę się rozpisywał. Kilka ciekawszych funkcji programu to wyznaczanie wartości średniej prędkości wiatru (z możliwością ustalenia z jakiego okresu ma być wyliczana średnia) oraz podmuchu (chwilowa maksymalna prędkość wiatru). Dodatkowo, jeżeli któryś sensor działa niepoprawnie (np. akcelerometr jest niepodłączony albo GPS nie ma fixa), to wartość z tego sensora jest wyświetlana w kolorze czerwonym. Widok okna programu: MECHANIKA Wszystkie obudowy i elementy mocujące zaprojektowałem w Autodesk Inventor i wykonałem z wykorzystaniem drukarki 3D. Na wyrzutni montowane są trzy urządzenia: jednostka główna, moduł kompasu i moduł akcelerometru. W czasie projektowania kierowałem się łatwością montażu w warunkach polowych (stąd np. zastosowanie nakrętek motylkowych do mocowania obejm) oraz zapewnienie możliwie wysokiej odporności na wodę. Render głównej obudowy: Render odbiornika: Nadajnik: Wnętrze nadajnika: Wnętrze odbiornika: Ciekawy patent, jaki mogę pokazać, to wykonanie złącz do świata zewnętrznego. Wykorzystałem złącza męskie KK254, które przylutowane są do odpowiednio wyciętej płytki prototypowej. W płytce są dwa otwory pod śruby M3, analogiczne otwory są w obudowie. Płytka jest mocowana od wewnątrz do ścianki urządzenia z wykorzystaniem dwóch śrub. Całość od zewnątrz wygląda elegancko i jest wytrzymała. Cała jednostka główna zmontowana: WNIOSKI I PODSUMOWANIE Projekt spełnia większość założeń i uważam, że został zrealizowany z sukcesem. Wykorzystany został już wielokrotnie na poligonie i w zasadzie za każdym razem system działał poprawnie. Jeśli chodzi o wady, to główny problem jaki zaobserwowałem, to uszkadzanie się modułów radiowych (konkretniej to wzmacniaczy mocy) w momencie włączenia urządzenia bez podłączonej anteny - co niestety zdarzyło się kilkukrotnie (przypadkowe włączenie). Drugi problem to brak pomiaru orientacji wyrzutni, związany z opisaną wcześniej kłopotliwą kalibracją. Przykładowy wykres uzyskany z wykorzystaniem stacji (na zawodach CanSatów w 2016 jeśli dobrze pamiętam): Krótki opis projektu jest też na mojej stronie internetowej: http://lukemeyer.me/rldaq.php Na koniec mogę pokazać ładne zdjęcie ze startu rakiety TuCAN (jak się dokładnie przyjrzycie, to widać stację zamontowaną na wyrzutni ). W projekcie TuCAN również brałem udział, zajmowałem się całością systemów elektronicznych w rakiecie. Jakby ktoś był zainteresowany, to mam mały opis na mojej stronie: http://lukemeyer.me/tucan.php
  14. Witam, Chciałbym przedstawić zbudowany ostatnio pojazd inspekcyjny. Założenia były następujące: dobra mobilność w nierównym terenie, sterowanie za pomocą aplikacji na Android'a oraz podgląd z wbudowanej kamery w czasie rzeczywistym. Mechanika W pojeździe zastosowano uproszczoną, 4-kołową wersję zawieszenia rocker-bogie stosowaną m.in. w łazikach marsjańskich. Główną zaletą tego rozwiązania jest niemal równomierny nacisk wszystkich kół na podłoże oraz możliwość uniesienia koła na przeszkodzie. Zastosowane koła pochodzą od "jakiejś" zabawki (znalezione na strychu ) i są zamocowane adapterem hex 12 mm (z małymi przeróbkami, aby schować przekładnię wewnątrz koła). Każde koło posiada własny silnik DC z przekładnią - prędkość wyjściowa: ok. 120 obr/min przy zasilaniu 12V. Silniki zostały zamocowane do aluminiowego profilu kwadratowego 10 mm za pomocą opasek zaciskowych. Profil ten stanowi część wahacza przymocowanego do osi kadłuba przez łożyska z wiertarek (średnica wewnętrzna 6 mm). Z tyłu pojazdu widoczna jest belka różnicowa łącząca wahacze po obu stronach. Dzięki niej kadłub utrzymywany jest w swojej pozycji a wychylenie jednego wahacza powoduje odchylenie drugiego o taki sam kąt przeciwnie skierowany. Jako kadłub wykorzystano obudowę z ABS. Do jej wieczka przymocowano również maszt z kamerą sterowany w dwóch osiach. Elektronika Komputerem sterującym w pojeździe jest Raspberry Pi Zero W z systemem Raspbian w wersji Jessie. Zastosowano ten model z powodu małych rozmiarów, stosunkowo niskiej ceny i małego poboru mocy. Z racji braku przetwornika ADC, zastosowano również arduino w wersji Pro Mini o napięciu 3.3V (aby było zgodne ze standardem w Raspberry). Są również 2 sterowniki silników na bazie modułu L298N (Sterownik ten ma dwa kanały i można było zastosować tylko jeden sterownik, jednak z powodu niewystarczającej wydajności prądowej zastosowano dwa), 2 przetwornice step-down 5V (osobno dla logiki, i osobno dla serwomechanizmów), dwa serwomechanizmy TowerPro SG90, kamera, oraz pakiet 3S ogniw li-ion w rozmiarze 18650. Z racji tego, że kamera jest podłączana przez taśmę FFC, zastosowano również przejściówki z FFC na goldpin, aby nie uszkodzić taśmy podczas obracania kamerą. Oprogramowanie Arduino w tym pojeździe odpowiedzialne jest za odczyt aktualnego napięcia zasilania oraz generowanie sygnałów PWM dla prędkości silników oraz serwomechanizmów pozycjonowania kamery. To jaki sygnał ma być generowany, jest wiadome dzięki połączeniu z Raspberry poprzez UART. Dodatkową funkcją Arduino jest wyłączenie PWM dla silników w przypadku braku komunikacji z Raspberry co zapobiega niekontrolowanej jeździe pojazdu w przypadku np. zerwania zasięgu. Raspberry komunikuje się z użytkownikiem poprzez sieć WiFi (Malinka działa w trybie hot-spot'u). Program działający na Raspberry został napisany w Python'ie i wykorzystuje również biblioteki Flask, w celu utworzenia serwera. Odpowiednie fragmenty kodu są wykonywane po wywołaniu przypisanego do niego adresu. Do transmisji wideo wykorzystano platformę Gstreamer, która pozwala na strumieniowanie w formacie H.264. Dzięki temu udało się uzyskać płynny obraz przy 30 FPS i rozdzielczości 800x600 cechujący się niewielkim opóźnieniem (ok. 300 ms). Powstała również dedykowana aplikacja na system Android, gdzie widoczny jest podgląd z kamery oraz sterowanie pojazdem. Podsumowanie Powstały pojazd zgodnie z założeniami dobrze radzi sobie w terenie, jednak pozostawia również spore możliwości rozbudowy. Można np. dodać zewnętrzną antenę WiFi aby poprawić zasięg (obecnie jest to ok 50m na otwartej przestrzeni), diodę doświetlającą, czy też różne czujniki. Najprawdopodobniej następnym krokiem będzie dodanie przetwarzania obrazów, aby pojazd był w stanie podążać za danym obiektem i być może omijać przeszkody. Na koniec krótka prezentacja działania:
  15. Woltomierz, amperomierz, watomierz, termometr, higrometr, barometr, czujnik zewnętrznej termopary, luksomierz w jednym urządzeniu. Projekt typu "weź wszystko co masz pod ręką, podłącz i zaprogramuj, będzie fajnie!" Dane logować można z terminala portu szeregowego (w tym do pliku - Windows/Linux). Wyniki pomiarów prezentowane są również na stronie WWW, a odczyty odświeżają się na żywo dzięki zastosowaniu technologii AJAX. Część do pomiaru prądu przypomina mój poprzedni projekt multimetru, przy czym wykorzystałem dokładniejszy moduł oparty na INA226 zamiast INA219. Podobnie jak tam zasilacz dla mierzonego obwodu podłączamy z boku do gniazda DC, a odbiornik "karmi się" z gniazd bananowych 4 mm. /* I2C D4 - SCL D3 - SDA */ #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> //#include <Adafruit_INA219.h> #include <INA226.h> //https://github.com/jarzebski/Arduino-INA226 #include <BH1750.h> //https://github.com/claws/BH1750 #include "max6675.h" //https://github.com/adafruit/MAX6675-library #include <LiquidCrystal_I2C.h> char* ssid = "Arduino Multimeter"; //const char *password = ""; //http://github.com/adafruit/MAX6675-library/issues/9#issuecomment-168213845 const int thermoDO = D6; const int thermoCS = D7; const int thermoCLK = D8; ESP8266WebServer server(80); Adafruit_BME280 bme; //Adafruit_INA219 ina219; INA226 ina226; BH1750 lightMeter; MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO); LiquidCrystal_I2C lcd(0x27, 20, 4); unsigned long lastTimeLCD = 0; void setup() { Serial.begin(9600); Wire.begin(D3, D4); Wire.setClock(100000); if (!bme.begin(0x76)) //changed from default I2C adress 0x77 { Serial.println("Nie odnaleziono czujnika BMP085 / BMP180"); while (1) { } } uint32_t currentFrequency; //ina219.begin(); // Default INA226 address is 0x40 ina226.begin(); // Configure INA226 ina226.configure(INA226_AVERAGES_1, INA226_BUS_CONV_TIME_1100US, INA226_SHUNT_CONV_TIME_1100US, INA226_MODE_SHUNT_BUS_CONT); // Calibrate INA226. Rshunt = 0.01 ohm, Max excepted current = 4A ina226.calibrate(0.01 * 1.318, 3); lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE); IPAddress apIP(192, 168, 1, 1); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); // WiFi.softAP(ssid, password); WiFi.softAP(ssid); server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); lcd.begin(); // lcd.backlight(); } void loop() { server.handleClient(); if (lastTimeLCD == 0 || millis() - lastTimeLCD >= 500) { lastTimeLCD = millis(); lcd.clear(); /* lcd.print(ina219.getBusVoltage_V()); lcd.print("V "); lcd.print(ina219.getCurrent_mA()); lcd.print("mA "); lcd.print(ina219.getPower_mW()); lcd.print("mW"); lcd.setCursor(0, 1); */ float volt = ina226.readBusVoltage(); float amp = 1000.0 * ina226.readShuntCurrent(); float power = 1000.0 * ina226.readBusPower(); int lux = lightMeter.readLightLevel(); float tempThero = thermocouple.readCelsius(); float temp = bme.readTemperature(); float hum = bme.readHumidity(); float pres = bme.readPressure(); lcd.print(volt); lcd.print("V "); lcd.print(amp, 1); lcd.print("mA "); lcd.setCursor(0, 1); lcd.print(power, 0); lcd.print("mW "); lcd.print(lux); lcd.print(" lx "); lcd.print(tempThero); lcd.print("C"); lcd.setCursor(0, 2); lcd.print(temp, 1); lcd.print("C "); lcd.print(hum, 1); lcd.print("% "); lcd.print(pres, 0); lcd.print("Pa"); Serial.println(String(volt) + "V " + String(amp, 1) + "mA " + String(power, 0) + "mW " + String(lux) + " lx " + String(tempThero) + "C " + String(temp, 1) + "C " + String(hum, 1) + "% " + String(pres, 0) + "Pa"); } } void handleRoot() { String content = "<html> <head><title>Ardunio Multimeter</title></head><body>"; content += "<DIV style=\"display:table; font-size: large;\"><DIV style=\"border-style: solid;\">BME280:<BR>Temperature: <span id=\"tempBME\"></span>C / <span id=\"tempBMEF\"></span>F<br>Humidity: <span id=\"humBME\"></span>%<br>Pressure: <span id=\"presBME\"></span>Pa<br></DIV><DIV style=\"border-style: solid;\">INA219:<BR>Voltage: <span id=\"voltage\"></span>V<br>Current: <span id=\"current\"></span>mA<br>Power: <span id=\"power\"></span>mW<br></DIV> <DIV style=\"border-style: solid;\">BH1750:<BR>Illuminance: <span id=\"illuminance\"></span>lx</DIV> <DIV style=\"border-style: solid;\">MAX6675:<BR>Temperature: <span id=\"thermocouple\"></span>C / <span id=\"thermocoupleF\"></span>F</DIV></DIV>"; content += "<script>myTimer();var myVar = setInterval(myTimer, 1000);function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(\";\"); document.getElementById(\"tempBME\").innerHTML = values[0]; document.getElementById(\"tempBMEF\").innerHTML = (values[0]*9/5 + 32).toFixed(2); document.getElementById(\"humBME\").innerHTML = values[1]; document.getElementById(\"presBME\").innerHTML = values[2]; document.getElementById(\"voltage\").innerHTML = values[3]; document.getElementById(\"current\").innerHTML = values[4]; document.getElementById(\"power\").innerHTML = values[5]; document.getElementById(\"illuminance\").innerHTML = values[6]; document.getElementById(\"thermocouple\").innerHTML = values[7]; document.getElementById(\"thermocoupleF\").innerHTML = (values[7]*9/5 + 32).toFixed(2);} }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();}</script>"; content += "</body></html>"; server.send(200, "text/html", content); } void handleSensors() { //String content = String(bme.readTemperature()) + ";" + String(bme.readHumidity()) + ";" + String(bme.readPressure()) + ";" + String(ina219.getBusVoltage_V()) + ";" + String(ina219.getCurrent_mA()) + ";" + String(ina219.getPower_mW()) + ";" + String(lightMeter.readLightLevel()) + ";" + String(thermocouple.readCelsius()) + ";"; String content = String(bme.readTemperature()) + ";" + String(bme.readHumidity()) + ";" + String(bme.readPressure()) + ";" + String(ina226.readBusVoltage()) + ";" + String(1000.0 * ina226.readShuntCurrent()) + ";" + String(1000.0 * ina226.readBusPower()) + ";" + String(lightMeter.readLightLevel()) + ";" + String(thermocouple.readCelsius()) + ";"; server.send(200, "text/plain", content); } Z zestawem łączy się prymitywna aplikacja napisana w C++. Potrafi zapisywać pomiary do pliku tekstowego i baz danych MySQL oraz SQLite. Jej pracę kończymy w bardzo "brutalny" sposób: Ctrl + C (podobnie, jak w loggerze dla poprzedniego multimetru). Przy pisaniu skorzystałem z poradnika (uwaga, strona zawiera dziwne, procesorożerne skrypty, które mogą nawet zawiesić przeglądarkę, radzę zatrzymać jej ładowanie [przycisk "X"] tuż po otwarciu) Server and client example with C sockets on Linux. Kompilacja: g++ webclient2mysql.cpp -L/usr/lib/mysql -lmysqlclient -l sqlite3 -o webclient2mysql -std=c++17 Jako parametry wywoływanego programu podajemy adres strony www urządzenia z wartościami pomiarów oraz interwał czasu (w sekundach), z jakim mają być zapisywane pomiary, np: ./webclient2mysql http://192.168.1.1/sensors 5 //g++ webclient2mysql.cpp -L/usr/lib/mysql -lmysqlclient -l sqlite3 -o webclient2mysql -std=c++17 //www.binarytides.com/server-client-example-c-sockets-linux/ /* #define DB_USER "" #define DB_PASSWORD "" #define DB_HOST "" #define DB_PORT 3307 #define DB_NAME "" */ #define DB_TABLE_NAME "logeddata" #define DB_SQLite_FILE "database.db" #define LOG_FILE "log.txt" #define CSV_DELIMITER ';' #define CSV_DELIMITER_COUNT 8 //Quantity of delimiters in one CSV row. #include <stdio.h> //printf #include <string.h> //strlen #include <sys/socket.h> //socket #include <arpa/inet.h> //inet_addr #include <unistd.h> #include <netdb.h> #include <iostream> #include <string> #include <vector> #include <chrono> #include <ctime> #include <thread> // std::this_thread::sleep_for #include <regex> #ifdef DB_HOST #include <mysql/mysql.h> //libmysqlclient-dev #endif #ifdef DB_SQLite_FILE #include <sqlite3.h> //sudo apt-get install sqlite3 libsqlite3-dev #endif #ifdef LOG_FILE #include <fstream> #endif const std::vector<std::string> explode(const std::string& s, const char& c); const std::string variables[][2] = { {"temperature", "float"}, {"humidity", "float"}, {"pressure", "float"}, {"voltage", "float"}, {"current", "float"}, {"power", "float"}, {"luminosity", "float"}, {"temperaturethermocouple", "float"}}; int main(int argc, char *argv[]) { if (argc != 3) { puts("Usage: ./webclient device_webpage_URL time_interval"); return 0; } const std::string s = argv[1]; std::smatch match; std::regex rgx("\/\/(.+?)\/"); std::string hostname, port; if (std::regex_search(s.begin(), s.end(), match, rgx)) { hostname = std::string(match[1]); if (hostname.find(":") != std::string::npos) { port = hostname.substr(hostname.find(":") + 1); hostname = hostname.substr(0, hostname.find(":")); } else { port = "80"; } } rgx = "\/\/.+?(\/.+)"; std::string path; if (std::regex_search(s.begin(), s.end(), match, rgx)) path = std::string(match[1]); std::cout << hostname << " " << port << " " << path << "\n"; int sock; struct sockaddr_in server; char server_reply[2000]; #ifdef DB_HOST MYSQL mysql; mysql_init(&mysql); if(mysql_real_connect(&mysql, DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, DB_PORT, NULL, 0)) printf("Connected to database!\n"); else printf("Can't connect to database: %d, %s\n", mysql_errno(&mysql), mysql_error(&mysql)); #endif #ifdef DB_SQLite_FILE char * error = 0; int rc; sqlite3 * db; sqlite3_stmt * steatment; sqlite3_open(DB_SQLite_FILE, & db); #endif #ifdef LOG_FILE std::ofstream myfile; myfile.open(LOG_FILE, std::ios::out | std::ios::app); #endif std::string query; #ifdef DB_HOST query = "CREATE TABLE IF NOT EXISTS " + std::string(DB_TABLE_NAME) +" (ID int NOT NULL AUTO_INCREMENT,"; for(int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++) { query += variables[i][0] + " " + variables[i][1] + ","; } query += "date_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (ID))"; std::cout << query << std::endl; mysql_query(&mysql, query.c_str()); #endif #ifdef DB_SQLite_FILE query = "CREATE TABLE IF NOT EXISTS " + std::string(DB_TABLE_NAME) +"(ID integer primary key,"; for(int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++) { query += variables[i][0] + " " + variables[i][1] + ","; } query += "date_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP)"; sqlite3_exec(db, query.c_str(), 0, 0, & error); // std::cout << error; #endif query = ""; while (1) { //Create socket sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { printf("Could not create socket"); } puts("Socket created"); server.sin_addr.s_addr = inet_addr(hostname.c_str()); server.sin_family = AF_INET; server.sin_port = htons(stoi(port)); //Connect to remote server if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) { perror("connect failed. Error"); return 1; } puts("Connected\n"); std::string message = "GET " + path + " HTTP/1.1\r\nHost: " + hostname + "\r\n\r\n"; puts(message.c_str()); //Send some data if (send(sock, message.c_str(), strlen(message.c_str()), 0) < 0) { puts("Send failed"); return 1; } std::string httpContent = ""; char cur; while ( read(sock, &cur, 1) > 0 ) { //Receive a reply from the server httpContent += cur; } httpContent = httpContent.substr(httpContent.find("\r\n\r\n") + 4); std::time_t currentTime = std::time(nullptr); std::vector<std::string> results; results = explode(httpContent, CSV_DELIMITER); std::cout << results.size() << "\n"; if(results.size() == CSV_DELIMITER_COUNT){ std::cout << currentTime << " " << httpContent << "\n"; #ifdef LOG_FILE myfile << currentTime << " "; for (int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++){ myfile << " " << results[i]; } myfile << " " << std::endl; #endif query = "INSERT INTO " + std::string(DB_TABLE_NAME) + " ("; for (int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++){ query += variables[i][0]; if(i < sizeof(variables) / sizeof(variables[0]) - 1) {query += ",";} } query += ") VALUES ("; for (int i = 0; i < sizeof(variables) / sizeof(variables[0]); i++){ query += "'" + results[i] + "'"; if(i < sizeof(variables) / sizeof(variables[0]) - 1) {query += ",";} } query += ")"; // query = "INSERT INTO " + std::string(DB_TABLE_NAME) + " (temperature, humidity, pressure" + ") VALUES ("+ "'" + std::string(results[0]) +"'," + "'" + std::string(results[1]) +"',"+ "'" + std::string(results[2]) +"'" +")"; #ifdef DB_HOST mysql_query(&mysql, query.c_str()); #endif #ifdef DB_SQLite_FILE sqlite3_exec(db, query.c_str(), 0, 0, & error); //std::cout << error; #endif } close(sock); std::this_thread::sleep_for(std::chrono::seconds(atoi(argv[2]))); } #ifdef LOG_FILE myfile.close(); #endif #ifdef DB_HOST mysql_close(&mysql); #endif #ifdef DB_SQLite_FILE sqlite3_close( db ); #endif return 0; } const std::vector<std::string> explode(const std::string& s, const char& c) //http://www.cplusplus.com/articles/2wA0RXSz/ { std::string buff{""}; std::vector<std::string> v; for(auto n:s) { if(n != c) buff+=n; else if(n == c && buff != "") { v.push_back(buff); buff = ""; } } if(buff != "") v.push_back(buff); return v; } Podgląd bazy SQLite z wynikami pomiarów (SQLite Browser) Składniki: ESP8266 wyświetlacz LCD 4x20 z konwerterem I2C LCM1602 moduł miernika napięcia i natężenia prądu z magistralą I²C INA226 BME280 czujnik termopary MAX6675 czujnik natężenia światła BH1750 bezpiecznik (zastosowałem PPTC 3A) gniazdo oraz wtyki bananowe 4 mm gniazdo DC 5.5/2.1 mm
  16. Kolega Robert (SP9-10126-KR) poprosił mnie o wykonanie sterownika do akwarium. Założeniem była "symulacja dnia" (płynne rozjaśnianie i ściemnianie oświetlenia LED o świcie i zmierzchu) oraz monitorowanie temperatury wody z alarmem (w razie awarii fabrycznej grzałki z termostatem do akwariów). Pracę urządzenia można doglądać za pomocą strony WWW. Parametry odświeżają się na żywo dzięki zastosowaniu technologii AJAX. Jeżeli temperatura nie znajdzie się w ustawionym przedziale zostanie włączony alarm. Czas synchronizowany jest automatycznie korzystając z serwera NTP. Ciekowostką jest możliwość konfiguracji nazywy SSID sieci WiFi i hasła do niej przez samego użytkownika poprzez stronę sterująca. Standardowo urządzenie łączy się z siecią WiFi. W przypadku, gdy zapamiętana konfiguracja jest niezgodna z parametrami sieci domowej, wystarczy nacisnąć i przytrzymać przycisk przed podłączeniem do prądu. ESP8266 uruchomi się w trybie Access Pointa, z którym będziemy mogli się połączyć i na stronie http://192.168.1.1 dokonamy poprawek. Po podłączeniu do sieci domowej WiFi możemy też nacisnąć wspomniany przycisk w trakcie pracy urządzenia, co pozwoli na wyświetlenie uzyskanego z DHCP IP. Ułatwi to wejście na stronę konfiguracyjną. Przy uruchomieniu następuje odczyt ustawień sieci #include <EEPROM.h> String ssid = "", pass = ""; [...] setup(){ if (ssid == "") { int i; for (i = 100; i < 170; i++) { if (EEPROM.read(i) == NULL) { break; } ssid += char(EEPROM.read(i)); } for (int ii = i + 1; ii < 250; ii++) { if (EEPROM.read(ii) == NULL) { break; } pass += char(EEPROM.read(ii)); } } WiFi.mode(WIFI_STA); WiFi.begin(ssid.c_str(), pass.c_str()); [...] server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); } Za odbieranie danych z formularza WWW i zapisywanie ich w pamięci odpowiada void handleRoot() { [...] if (server.hasArg("SSID") && server.arg("SSID") != "" && server.hasArg("wifipass") && server.arg("wifipass") != "") { for (i = 0; i < server.arg("SSID").length(); i++) { EEPROM.write(i + 100, server.arg("SSID")[i]); } i += 100; EEPROM.write(i, NULL); for (i = 0; i < server.arg("wifipass").length(); i++) { EEPROM.write(i + 101 + server.arg("SSID").length(), server.arg("wifipass")[i]); } EEPROM.write(server.arg("SSID").length() + server.arg("wifipass").length() + 101, NULL); EEPROM.commit(); ESP.restart(); } } Cały kod wygląda następująco: #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <EEPROM.h> #include <WiFiUdp.h> #include <Time.h> // http://www.pjrc.com/teensy/td_libs_Time.html #include <ESP8266mDNS.h> #include <OneWire.h> //https://github.com/adafruit/ESP8266-Arduino/tree/esp8266/libraries/OneWire #include <DallasTemperature.h> //https://github.com/milesburton/Arduino-Temperature-Control-Library #include <Wire.h> #include <LiquidCrystal_I2C.h> //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library #define PWMpin 15 //(D8) #define WiFimodeButton 12 //D6 #define PumpPin 13 //(D7) #define TempAlarmPin 14 //(D5) #define ONE_WIRE_BUS 4 //D2 /*char ssid[] = ""; // your network SSID (name) char pass[] = ""; // your network password */ String ssid = "", pass = ""; const char* WiFiHostname = "aquarium"; unsigned char screenCount = 0, tmBegin, tmEnd, dusk, pumpBegin, pumpEnd, tempMin, tempMax; bool lastTimeLCD, tempAlarm, lastButtonStatus = HIGH; unsigned int localPort = 2390; // local port to listen for UDP packets int PWM = 0; float temp = 0; /* Don't hardwire the IP address or we won't get the benefits of the pool. Lookup the IP address for the host name instead */ //IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server IPAddress timeServerIP; // time.nist.gov NTP server address const char* ntpServerName = "time.nist.gov"; const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets // A UDP instance to let us send and receive packets over UDP WiFiUDP udp; ESP8266WebServer server(80); OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); LiquidCrystal_I2C lcd(0x27, 16, 2); tmElements_t tm; time_t getNtpTime(); unsigned long last_time_sync = 0, last_NTP_check = 0; void setup() { pinMode(PWMpin, OUTPUT); pinMode(WiFimodeButton, INPUT_PULLUP); pinMode(TempAlarmPin, OUTPUT); Serial.begin(9600); EEPROM.begin(512); Wire.begin(0, 2); //D2, D4 Wire.setClock(100000); sensors.begin(); lcd.begin(); lcd.backlight(); tmBegin = EEPROM.read(0); tmEnd = EEPROM.read(1); dusk = EEPROM.read(2); pumpBegin = EEPROM.read(3); pumpEnd = EEPROM.read(4); tempAlarm = EEPROM.read(5); tempMin = EEPROM.read(6); tempMax = EEPROM.read(7); // We start by connecting to a WiFi network // WiFi.mode(WIFI_AP_STA); WiFi.softAPdisconnect(); WiFi.disconnect(); if (ssid == "") { int i; for (i = 100; i < 170; i++) { if (EEPROM.read(i) == NULL) { break; } ssid += char(EEPROM.read(i)); } for (int ii = i + 1; ii < 250; ii++) { if (EEPROM.read(ii) == NULL) { break; } pass += char(EEPROM.read(ii)); } } if (digitalRead(WiFimodeButton) == LOW) { //access point part Serial.println("Creating Accesspoint"); IPAddress apIP(192, 168, 1, 1); WiFi.mode(WIFI_AP); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); WiFi.softAP("aquarium", "aquarium123"); Serial.print("IP address:\t"); Serial.println(WiFi.softAPIP()); } else { //station part Serial.print("connecting to..."); Serial.println(ssid); WiFi.hostname(WiFiHostname); WiFi.mode(WIFI_STA); WiFi.begin(ssid.c_str(), pass.c_str()); /* while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } */ Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } /* MDNS.begin("aquarium"); MDNS.addService("http", "tcp", 80);*/ Serial.println("Starting UDP"); udp.begin(localPort); Serial.print("Local port: "); Serial.println(udp.localPort()); server.on("/", handleRoot); server.on("/sensors", handleSensors); server.begin(); } void loop() { Serial.println(ssid); Serial.println(pass); Serial.println(WiFi.status()); if (millis() - last_time_sync > 10 * 24 * 60 * 60 * 1000 || last_time_sync == 0) { unsigned long unixt = unix_time(); if (unixt > 1512876158) { setTime(unixt); last_time_sync = millis(); } } Serial.print("Time library "); Serial.print(current_time(0)); breakTime(now(), tm); if (now() < 1512876158) { PWM = 0; } else if (tmBegin < tmEnd && tm.Hour >= tmBegin && tm.Hour < tmEnd || tmBegin > tmEnd && (tm.Hour >= tmBegin || tm.Hour < tmEnd)) { if (tm.Hour * 60 + tm.Minute < tmBegin * 60 + dusk || tm.Hour * 60 + tm.Minute < tmEnd * 60 - dusk) { PWM = 1023 - map(tmBegin * 60 * 60 + dusk * 60 - (tm.Hour * 60 * 60 + tm.Minute * 60 + tm.Second), 0, dusk * 60, 0, 1023); } if (tm.Hour * 60 + tm.Minute > tmEnd * 60 - dusk) { if (tmBegin < tmEnd) { PWM = 1023 - map(tm.Hour * 60 * 60 + tm.Minute * 60 + tm.Second - (tmEnd * 60 * 60 - dusk * 60), 0, dusk * 60, 0, 1023); } else { PWM = map(tmEnd * 60 * 60 - (tm.Hour * 60 * 60 + tm.Minute * 60 + tm.Second), 0, dusk * 60, 0, 1023); } } if (tm.Hour * 60 + tm.Minute >= tmBegin * 60 + dusk && tm.Hour * 60 + tm.Minute <= tmEnd * 60 - dusk || tmBegin > tmEnd && (tm.Hour * 60 + tm.Minute > tmBegin * 60 + dusk || tm.Hour * 60 + tm.Minute < tmEnd * 60 - dusk)) { PWM = 1023; } } /*else if (tmBegin < tmEnd && tm.Hour > tmBegin && tm.Hour < tmEnd || tmBegin > tmEnd && (tm.Hour > tmBegin || tm.Hour < tmEnd)) { PWM = 1023; } else if (tm.Hour == tmBegin) { PWM = map(tm.Minute, 0, 59, 0, 1023); } else if (tm.Hour == tmEnd) { PWM = map(59 - tm.Minute, 0, 59, 0, 1023); }*/ else { PWM = 0; } analogWrite(PWMpin, PWM); Serial.print(" "); Serial.println(PWM); server.handleClient(); if (now() % 2 != lastTimeLCD) { lastTimeLCD = now() % 2; sensors.requestTemperatures(); if (digitalRead(WiFimodeButton) == LOW && lastButtonStatus == HIGH && WiFi.status() == WL_CONNECTED) { screenCount++; } lastButtonStatus = digitalRead(WiFimodeButton); temp = sensors.getTempCByIndex(0); if (tempAlarm && (temp < (float)tempMin || temp > (float)tempMax)) { digitalWrite(TempAlarmPin, HIGH); } else { digitalWrite(TempAlarmPin, LOW); } if (tm.Hour >= pumpBegin && tm.Hour < pumpEnd) { digitalWrite(PumpPin, HIGH); } else { digitalWrite(PumpPin, LOW); } lcd.clear(); switch (screenCount % 2) { case 0: lcd.print(current_time(1)); lcd.setCursor(0, 1); lcd.print(temp, 1); lcd.print("C Day"); lcd.print(tmBegin); lcd.print("-"); lcd.print(tmEnd); break; case 1: lcd.print(WiFi.localIP().toString()); break; } } } void handleRoot() { int i; if (server.hasArg("begin") && server.arg("begin").toInt() >= 0 && server.arg("begin").toInt() <= 24 && server.hasArg("end") && server.arg("end").toInt() >= 0 && server.arg("end").toInt() <= 24 && server.hasArg("dusk") && server.arg("dusk").toInt() >= 0 && server.arg("dusk").toInt() <= 255) { tmBegin = server.arg("begin").toInt(); tmEnd = server.arg("end").toInt(); dusk = server.arg("dusk").toInt(); EEPROM.write(0, tmBegin); EEPROM.write(1, tmEnd); EEPROM.write(2, dusk); if (server.hasArg("pumpBegin") && server.arg("pumpBegin").toInt() >= 0 && server.arg("pumpBegin").toInt() <= 24 && server.hasArg("pumpEnd") && server.arg("pumpEnd").toInt() >= 0 && server.arg("pumpEnd").toInt() <= 24) { pumpBegin = server.arg("pumpBegin").toInt(); pumpEnd = server.arg("pumpEnd").toInt(); EEPROM.write(3, pumpBegin); EEPROM.write(4, pumpEnd); } // if (server.arg("tempMin").toInt() >= 0 && server.arg("tempMin").toInt() <= 100 && server.hasArg("tempMax") && server.arg("tempMax").toInt() >= 0 && server.arg("tempMax").toInt() <= 100 && server.arg("tempMax").toInt() > server.arg("tempMin").toInt()) if (server.arg("tempMin").toInt() >= 0 && server.arg("tempMin").toInt() <= 100 && server.arg("tempMax").toInt() >= 0 && server.arg("tempMax").toInt() <= 100 && server.arg("tempMax").toInt() > server.arg("tempMin").toInt()) { tempAlarm = server.arg("tempAlarm").toInt(); tempMin = server.arg("tempMin").toInt(); tempMax = server.arg("tempMax").toInt(); EEPROM.write(5, tempAlarm); EEPROM.write(6, tempMin); EEPROM.write(7, tempMax); } EEPROM.commit(); } if (server.hasArg("SSID") && server.arg("SSID") != "" && server.hasArg("wifipass") && server.arg("wifipass") != "") { for (i = 0; i < server.arg("SSID").length(); i++) { EEPROM.write(i + 100, server.arg("SSID")[i]); } i += 100; EEPROM.write(i, NULL); for (i = 0; i < server.arg("wifipass").length(); i++) { EEPROM.write(i + 101 + server.arg("SSID").length(), server.arg("wifipass")[i]); } //i += 3 + server.arg("wifipass").length(); EEPROM.write(server.arg("SSID").length() + server.arg("wifipass").length() + 101, NULL); EEPROM.commit(); ESP.restart(); } String content = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"><title>Aquarium controller</title></head><body>"; /* for (i = 2; i < 100; i++) { if (EEPROM.read(i) == 5) { break; } content += char(EEPROM.read(i)); } content += ";"; for (int ii = i + 1; ii < 150; ii++) { if (EEPROM.read(ii) == 5) { break; } content += char(EEPROM.read(ii)); } */ content += "<DIV style=\"display:table; font-size: large; border-style: solid;\">Current time: <span id=\"time\"></span><BR>Temperature: <span id=\"temp\"></span>°C<BR>PWM: <span id=\"PWM\"></span>%<br>"; // content += "<div id=\"refresh\"> </div><script>var myVar = setInterval(myTimer, 1000); function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { document.getElementById(\"refresh\").innerHTML = this.responseText;} }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();}</script>"; content += "\"Artificial day\" from " + String((int)tmBegin) + ":00 to " + String((int)tmEnd) + ":00. Dusk duration: " + String((int)dusk) + " minutes.</DIV>"; content += "<script>myTimer();var myVar = setInterval(myTimer, 1000);function myTimer() {var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { var str = this.responseText; var values = str.split(\";\"); document.getElementById(\"time\").innerHTML = values[0]; document.getElementById(\"temp\").innerHTML = values[1]; document.getElementById(\"PWM\").innerHTML = values[2]; } }; xhttp.open(\"GET\", \"sensors\", true); xhttp.send();} var i = 0;</script>"; content += "<form action='/' method='POST'>Begin: <input type='number' pattern='[0-9]' maxlength='2' size='2' name='begin' value='" + String((int)tmBegin) + "'> UTC<br>"; content += "End: <input type='number' pattern='[0-9]' maxlength='2' size='2' name='end' value='" + String((int)tmEnd) + "'> UTC<br>"; content += "Dusk: <input type='number' pattern='[0-9]' maxlength='2' size='2' name='dusk' value='" + String((int)dusk) + "'> minutes<br>"; content += "Air pump operates between <input type='number' pattern='[0-9]' maxlength='2' size='2' name='pumpBegin' value='" + String((int)pumpBegin) + "'> and <input type='number' pattern='[0-9]' maxlength='2' size='2' name='pumpEnd' value='" + String((int)pumpEnd) + "'><br>"; content += "<input type=\"checkbox\" name=\"tempAlarm\" value=\"1\""; if (tempAlarm) { content += " checked"; } content += ">TempAlarm <input type='number' pattern='[0-9]' maxlength='2' size='2' name='tempMin' value='" + String((int)tempMin) + "'> - <input type='number' pattern='[0-9]' maxlength='2' size='2' name='tempMax' value='" + String((int)tempMax) + "'><br>"; content += "<a href=\"\" onclick=\"i = i+1; if(i % 2 == 0) {wifi.style.display = 'none';} else {wifi.style.display = 'block';} return false;\">Change WiFi settings</a>"; content += "<div id=\"wifi\" style=\"display:none;\">Home WiFi Network Name (SSID): <input type='text' name='SSID' value='" + String(ssid) + "'><br>"; content += "Password: <input type='password' name='wifipass'></div><br>"; content += "<input type='submit' value='Submit'></form> <br>"; content += "</body></html>"; server.send(200, "text/html", content); } void handleSensors() { String content = current_time(0) + ";" + String(temp) + ";" + String(100.0 * PWM / 1023.0); server.send(200, "text/html", content); } String current_time(bool shortstring) { breakTime(now(), tm); String timeStr; if (!shortstring) { if (tm.Year > 0) { tm.Year -= 30; } else { tm.Year = 0; } } timeStr = String(tm.Day) + "." + String(tm.Month); if (!shortstring) { timeStr += "." + String(tm.Year); } timeStr += " " + String(tm.Hour) + ":"; if (tm.Minute < 10) { timeStr += "0"; } timeStr += String(tm.Minute) + ":"; if (tm.Second < 10) { timeStr += "0"; } timeStr += String(tm.Second) + "UTC"; return timeStr; } // send an NTP request to the time server at the given address //https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/NTPClient/NTPClient.ino unsigned long sendNTPpacket(IPAddress& address) { //Serial.println("sending NTP packet..."); // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: udp.beginPacket(address, 123); //NTP requests are to port 123 udp.write(packetBuffer, NTP_PACKET_SIZE); udp.endPacket(); } time_t unix_time() { if (WiFi.status() == WL_CONNECTED && ((unsigned long)(millis() - last_NTP_check) > 15000 || last_time_sync == 0)) { last_NTP_check = millis(); //get a random server from the pool WiFi.hostByName(ntpServerName, timeServerIP); sendNTPpacket(timeServerIP); // send an NTP packet to a time server // wait to see if a reply is available delay(1000); int cb = udp.parsePacket(); if (!cb) { //Serial.println("no packet yet"); } else { //Serial.print("packet received, length="); //Serial.println(cb); // We've received a packet, read the data from it udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer //the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // combine the four bytes (two words) into a long integer // this is NTP time (seconds since Jan 1 1900): unsigned long secsSince1900 = highWord << 16 | lowWord; //Serial.print("Seconds since Jan 1 1900 = " ); //Serial.println(secsSince1900); // now convert NTP time into everyday time: Serial.println("Unix time sync "); // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: const unsigned long seventyYears = 2208988800UL; // subtract seventy years: unsigned long epoch = secsSince1900 - seventyYears; // print Unix time: if (epoch > 1512876158) { return epoch + 2; } else { return 0; } } } } Składniki: ESP8266 wodoodporny termometr oparty na układzie DS18B20 wyświetlacz LCD 2x16 z konwerterem I2C LCM1602 przetwornica step-down LM2596 zapewniająca zasilanie 3.3V dla elektroniki, konwertując z 12V na wejściu N-kanałowy tranzystor MOSFET do sterowania PWM oświetleniem LED buzzer
  17. Andrzejbrzez

    Arduino losowe sterowanie servem

    modified 8 Nov 2013 by Scott Fitzgerald http://www.arduino.cc/en/Tutorial/Sweep */ #include <Servo.h> Servo myservo; // create servo object to control a servo // twelve servo objects can be created on most boards int pos = 0; // variable to store the servo position void setup() { myservo.attach(9); // attaches the servo on pin 9 to the servo object } void loop() { for (pos = 0; pos <= 100; pos += 1) { // goes from 0 degrees to 180 degrees // in steps of 1 degree myservo.write(pos); // tell servo to go to position in variable 'pos' delay(35); // waits 15ms for the servo to reach the position } for (pos = 100; pos >= 0; pos -= 1) { // goes from 180 degrees to 0 degrees myservo.write(pos); // tell servo to go to position in variable 'pos' delay(35); // waits 15ms for the servo to reach the position } delay(1000); } Witam serdecznie Jestem tu nowy i kompletnie zielony i liczę na Waszą pomoc Jest to program wgrany z przykładów do serva Troche zmieniłem kąty i czasy Oraz ostatnia komende którą wpisałem delay(1000); robi mi przerwy miedzy kolejnymi ruchami serwa moje proźby o pomoc sa nastepujące By ostatnia komenda delay chodziła losowo w zakresie czasu 1000 - 3000 By cała pętla powtarzała się losową ilość razy w zakresie 10- 50 ruchów serwa By po każdej takiej pętli - powiedzmy serwo robi losowo 25 ruchów następowała przerwa zanim program zacznie wykonywać kolejną pętle i tu też losowo czasy od 1 do 6 minut
  18. KubekSzklany

    Monitor Zasobów Na Arduino

    Witam. Chciałbym przedstawić mój projekt, mianowicie monitor zasobów (coś jak zakładka wydajność w menadżerze zadań) wyświetlany na wyświetlaczu Arduino. Potrzebne Rzeczy: Jest to bardzo prosty projekt do którego budowy potrzebujemy zaledwie 2 rzeczy: Arduino Uno (można też użyć np. Arduino Uno WiFi aby połączyć się przez wifi lub Arduino Mega gdyby brakowało nam pinów) Wyświetlacz dotykowy. Ja użyłem nakładki tzw. shielda na arduino, jednak jeżeli chcemy ograniczyć liczbę używanych pinów można użyć dowolnego wyświetlacza, przykładowo takiego, ja użyłem ekranu o rozdzielczości 240x320 pikseli. No i wiadomo kabel do połączenia z komputerem. Tak wygląda mój zestaw: Cel Działania: Celem tego wyświetlacza jest pokazywanie aktualnego obciążenia systemu, bez ciągłego patrzenia na menadżer zadań i konieczności jego ciągłego działania. Możemy np. podczas gry w jakąś wymagająca grę patrzeć, czy czasem nie palimy karty graficznej Tutaj screen podczas gry w gta v, jak widać moja karta graficzna chodzi na pełnych obrotach: Działanie: Program uruchamiany na komputerze wysyła dane do arduino, które następnie wyświetla odpowiednią ilość kwadratów. Tak to mniej więcej działa: Tutaj widać porównanie menadżera zadań windows i arduino - jest lekkie opóźnienie ok. 1s, prawdopodobnie wynikające z tego, że program wysyła dane w innym czasie niż menedżer zadań, jednak myślę że ta jedna sekunda nic nie zmienia. A z kolei tutaj widać odpowiednio - zużycie procesora podczas kopania btc, dane, które przechodzą przez sieć i zużycie dysku podczas przenoszenia dużych plików. Program: Jeżeli chodzi o program na arduino to wszystko zależy od tego jak ktoś zaplanuje wygląd i zależy też dużo od sterownika wyświetlacza. Przyjmowanie danych to odczytywanie kolejnych wartości serial'u, zaczynając od procesora, a kończąc na internecie. Natomiast żeby arduino znało zużycie poszczególnych podzespołów należy uruchomić program na komputerze. W tym celu trzeba stworzyć aplikację działającą w tle (ja wybrałem aplikację konsolową c#) i utworzyć skrót do aplikacji w menu start. Tym sposobem program uruchomi się nam przy starcie systemu. A co zawiera program? Na początku otwarcie serial'u: SerialPort Serial = new SerialPort("COM6", 9600); Serial.Open(); COM6 oznacza numer portu (ten sam co w programie arduino), 9600 to bitrate. Aby wysłać dane do arduino wystarczy: while(true) { // Wysyłanie zużycia procesora w skali 1-10: PerformanceCounter ProcessorUsage = new PerformanceCounter("Processor", "% Processor Time", "_Total"); Serial.Write(Math.Floor(ProcessorUsage.NextValue() * 0.1)); // Wysyłanie zuzycia karty graficznej w skali 1-10: Computer = new Computer() { GPUEnabled = true }; Computer.Open(); foreach (var HardwareItem in Computer.Hardware) { if (HardwareItem.HardwareType == HardwareType.GpuNvidia) { HardwareItem.Update(); foreach (var SubHardware in HardwareItem.SubHardware) { SubHardware.Update(); } double Numbers = 0; int Number = 0; foreach (var Sensor in HardwareItem.Sensors) { if (Sensor.SensorType == SensorType.Load) { Numbers = Numbers + Sensor.Value.Value; Number++; } } Serial.Write(Math.Floor((Numbers / Number) * 0.1)); } } // Wysyłanie użycia sieci w Mb/s: NetworkInterface[] NetInterfaces = NetworkInterface.GetAllNetworkInterfaces(); long BytesDown = NetInterfaces[0].GetIPv4Statistics().BytesReceived; long BytesSend = NetInterfaces[0].GetIPv4Statistics().BytesSent; } Jak widać zużycie procesora (a także pamięci czy dysku) możemy pobrać za pomocą performance counter. Dla innych podzespołów wystarczy zmienić nazwy w cudzysłowach. Karta graficzna jest trochę bardziej skomplikowana i trzeba obliczyć średnią dla różnych elementów, tzn. zużycie procesora graficznego, zużycie pamięci graficznej itd. Uwaga: Tutaj potrzebna będzie zewnętrzna biblioteka, tzn. OpenHardwareMonitor. Można ją pobrać np. menadżerem pakietów NuGet. A ostatnie 3 linie to wysłane i odebrane bajty. Jednak trzeba wziąć pod uwagę że jest to naliczane od startu komputera, więc trzeba sobie co sekundę sprawdzić o ile zwiększyła się ta liczba. Na Zakończenie: Tutaj taki krótki film z działania wyświetlacza.
  19. Witam wszystkich chciałbym Wam dzisiaj pokazać sejf mojego autorstwa. Może on być głównie wykorzystywany jako ciekawostka lub zabawka. Projekt powstał w celu nauczenia się podstaw arduino. Ostatnio nauczyłem się sterowania silniczkami oraz przechwytywania liczb z klawiatury. Po połączeniu tych umiejętności z podstawami programowania powstał ten projekt sejfu. Przy tworzeniu tego projektu wykorzystałem następujące materiały: Arduino Uno Klawiaturę numeryczną Serwo potencjometr Przewody połączeniowe męsko-damskie Wykonanie: Projekt został wykonany z elementów jakie posiadałem w domu. Do Arduino podłączone są przewody, które łączą się z serwem (zasilanie, masa oraz sygnał sterujący). Przewody z wyświetlacza są podłączone bezpośrednio do Arduino poza V0. Ponadto, V0 zostało zintegrowane z potencjometrem i służy do regulacji kontrastu wyświetlacza. Układ jest zasilany baterią 9V za pomocą odpowiedniego adaptera Działanie projektu: Program przechwytuje z klawiatury kod wpisywany przez użytkownika. Jeśli kod jest prawidłowy to uruchamia się serwo i otwiera skrytkę. Jeśli kod jest nie prawidłowy program informuje użytkownika o niepoprawnym haśle. Na klawiaturze znajdują się klawisze A, B, C, D, # i *. Mój program ich nie wykorzystuje, ale jeśli ktoś chce wykonać taki projekt to może je wykorzystać. Po kliknięciu wyżej wymienionych klawiszy program informuje o naciśnięciu złego klawisza. Kod: #include <Servo.h> #include <LiquidCrystal.h> // biblioteka do LCD #include <Keypad.h> //biblioteka do klawiatury Servo servo; int pos = 40; char* password ="7890"; //hasło int pozisyon = 0; int przycisk = 0; const byte rows = 4; const byte cols = 4; char keyMap [rows] [cols] = { {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'} }; byte rowPins [rows] = {1, 2, 3, 4}; byte colPins [cols] = {5, 6, 7, 8}; Keypad myKeypad = Keypad( makeKeymap(keyMap), rowPins, colPins, rows, cols); LiquidCrystal lcd (A0, A1, A2, A3, A4, A5); void setup(){ servo.attach(9); lcd.begin(16, 2); setLocked (true); przycisk = 0; } void loop(){ setLocked (true); char whichKey = myKeypad.getKey(); lcd.setCursor(0, 0); lcd.print(" Witam"); lcd.setCursor(0, 1); lcd.print(" Wpisz haslo"); if(whichKey == '*' || whichKey == '#' || whichKey == 'A' || whichKey == 'B' || whichKey == 'C' || whichKey == 'D'){ przycisk=0; pozisyon=0; setLocked (true); lcd.clear(); lcd.setCursor(0, 0); lcd.print("ZLY PRZYCISK"); delay(1000); lcd.clear(); } if(whichKey == '0' || whichKey == '1' || whichKey == '2' || whichKey == '3' || //define keys whichKey == '4' || whichKey == '5' || whichKey == '6' || whichKey == '7' || whichKey == '8' || whichKey == '9'){ przycisk++; } if(przycisk == 5){ przycisk = 0; lcd.clear(); lcd.setCursor(0,0); lcd.write(" ZA DUZO"); lcd.setCursor(0,1); lcd.write(" ZNAKOW"); delay(1000); lcd.clear(); } if(przycisk == 1){ lcd.clear(); lcd.setCursor(0,1); lcd.write(" *"); } if(przycisk == 2){ lcd.clear(); lcd.setCursor(0,1); lcd.write(" **"); } if(przycisk == 3){ lcd.clear(); lcd.setCursor(0,1); lcd.write(" ***"); } if(przycisk == 4){ lcd.clear(); lcd.setCursor(0,1); lcd.write(" ****"); } if(przycisk == 4 && pozisyon < 3){ lcd.clear(); lcd.setCursor(0,0); lcd.write(" ZLE"); lcd.setCursor(0,1); lcd.write(" HASLO"); delay(1000); przycisk = 0; lcd.clear(); } if(whichKey == password [pozisyon]){ pozisyon ++; } if(pozisyon == 4){ przycisk = 0; pozisyon = 0; setLocked (false); lcd.clear(); lcd.setCursor(0, 0); lcd.print("*** Verified ***"); delay(3000); lcd.clear(); lcd.setCursor(0, 0); lcd.print("Skrytka zamknie"); lcd.setCursor(0, 1); lcd.print(" sie za 5s"); delay(7000); lcd.clear(); } delay(100); } void setLocked(int locked){ if(locked){ servo.write(40); } else{ przycisk = 0; servo.write(130); } } Przyszłość projektu: Projekt można modyfikować (np. zmiana hasła przez użytkownika). Można też zmienić rodzaj zabezpieczenia z kodu czterocyfrowego na hasło z literami lub odcisk palca użytkownika. W najbliższej przyszłości zamierzam to połączyć z Raspberry Pi (rozpoznawanie twarzy) i Google AIY Voice Kit (komendy głosowe). Film: Zdjęcia:
  20. Co może zrobić uczeń podstawówki, kiedy nie można w szkole używać telefonów? Specjalny kalkulator! Tak, wiem że urządzenie jest trochę przekombinowane - za pare zł mam nowy kalkulator z kiosku, ale to nie jest zwykły kalkulator.Ten kalkulator, albowiem, robiłem ja Budowa Urządzenie posiada wyświetlacz TFT 1.8" ze slotem na kartę pamięci, z której możemy odtwarzać zdjęcia. Do wprowadzania informacji posłużyła mi klawiatura 4x4 w formie naklejki. Całość napędza Arduino Pro Mini, które przeprogramowałem na korzystanie z wewnętrznego kwarcu 8Mhz i wyłączenie BOD. Energię dostarcza akumulator Lipo wyjęty z tableta, ładowany przez dolutowanie się do akusa. Napięcie obniża stabilizator AMS1117 na 3,3v, co rozwiązało kwestię napięcia logiki (TFT ma 3.3v, arduino normalnie 5v) oraz zużycia energii. Wyświetlacz, do programowania, można zdejmować. Po prawej stronie dostrzegamy przełącznik włączający urządzenie, zaś pod TFT znajduje się przycisk funkcyjny. Obudowy nie chciałem robić, gdyż chciałem wzbudzić zainteresowanie wśród kolegów Funkcje Kalkulator ma funkcję liczenia. Wow. Potrafi dzielić, mnożyć, dodawać i odejmować liczby dziesiętne, dodatnie i ujemne - ale nie umie wykonywać kolejności działań (dlatego według kalkulatora 2+2x2=8). ALE. Oprócz kalkulatora, utworzyłem aplikację paint - służy do malowania piksel po pikselu. Dobre na nudne lekcje polskiego. Po włączeniu urządzenia, wyświetla nam się lista dostępnych aplikacji. Oprócz niedokończonego snejka, jest jeszcze "czwarta aplikacja" - ściąga Wyświetla ona wzory z fizyki, chemii i matematyki oraz prawa dynamiki Newtona. Kalkulator może również w progmemie przechować jedno kolorowe zdjęcie, i wyświetlić je po 8x naciśnięciu przycisku Równa się. Taki kalkulator to fajna rzecz, z racji tego że można go używać w szkole bez ograniczeń i da się na nim pobawić. W ostateczności przeglądać memy, po włożeniu karty SD. Pozdrawiam, Leoneq :3
  21. Metod samodzielnego wykonywania płytek pcb jest wiele. Termotransfer - potrzebna dobra drukarka laserowa, fototransfer - też dobra drukarka, płytki światłoczułe lub własnoręczne pokrywanie takim lakierem, suszenie, naświetlanie itp. Testowałem naświetlanie i albo prześwietliło albo odwrotnie. Można frezować. Ale można też rysować. Oczywiście nie ręcznie, jak to dawniej bywało. Jako, że z cnc i budową urządzeń mam trochę do czynienia, postanowiłem zbudować sobie taką małą maszynkę. Większość materiałów miałem - aluminiowe płytki, kątowniki, silniki krokowe i szlifowane prowadnice ze starych drukarek, a także dostęp do własnej małej tokarko - frezarki cnc. Całość miała być "po taniości" i maksymalne wykorzystanie "przydasiów" . Początkowo użyłem kilku gotowych podzespołów, jak stolik krzyżowy (proxxon) i mechanizm napędu ze starego CD/DVD jako oś Z. Użyte silniczki mają 24 kroki/obrót, zasilane z 12 V. Ich sterowniki to DRV8825 . Koła zębate pod paski też wyrób własny - frezowanie ( aluminium i poliamid ), paski trzeba było nabyć. Całością steruje Arduino Uno i GRLB1.1. Problem pojawił się w momencie zainstalowania krańcówek. Jedna oś działała, reszta martwa. CNC Shield był projektowany pod GRLB-8, a nowsze wersje tego oprogramowania mają pozmieniane niektóre wyprowadzenia. Ale jest to opisane na stronie GRLB. Nic nie pomagało - wgrywanie softu, czyszczenie procka i jego EEPROM'u. Oczywiście kontrola przewodów i samych krańcówek. Do "przyzwoitego" połączenia przewodów z układem sterującym przydają się złącza BLS. Dopiero nowa Atmega328 - i teraz wszystko gra. Nie spodziewałem się wadliwego układu. W ferworze walki o działające krańcówki została wykonana optoizolacja na układzie LTV847 . Płytkę do tego wyrysowała maszyna już sama dla siebie. Ponieważ wszystko działało, można by tak zostawić. Jednak pole robocze było małe - 45 x 120 mm. Trochę pracy trzeba było włożyć - cięcie, frezowanie i toczenie elementów. Ale warto było, bo powstałą dość solidna konstrukcja - już tak przyszłościowo.Dorobienie śruby i nakrętki (żeliwo) z kasowaniem luzu, mocowania silnika i łożyskowania śruby dopełnia reszty. Oczywiście dokładne ustawienie na czujnik zegarowy i dokładnym kątomierzem równoległości oraz kątów. W efekcie obszar roboczy zwiększył się do 180 x 120 mm. Większych płytek chyba robić nie będę. Mocowanie pisaka umożliwia ruch góra/dół - taki luz bezpieczeństwa - z uwagi na czasem spotykane niezbyt "płaskie" laminaty, natomiast luz poprzeczny jest minimalny i wg. pomiarów nie przekracza 0,03mm. Można więc mówić o precyzji . Oczywiście kusi przeróbka. W pierwszym rzędzie zmiana silników na taki krokowiec. Obecnie używane mają cienką oś - 2 mm, która wygina się od naciągniętych pasków. Ich łożyska cierne też się od tego mocno wyrabiają jednostronnie. Drugi powód - mocniejszy silnik umożliwi większą szybkość pracy. Obecnie to 300 mm/min. Nowa oś Z ( mocniejsza ) i zwiększenie jej zakresu pracy oraz obniżenie stołu roboczego. Zamiast obecnego mazaka - bo tylko tyle ten mały silniczek dźwignie - można by założyć jakiś moduł lasera albo i głowicę drukującą ( 3D ). Albo i mały silnik jako wiertarkę. W chwili obecnej pisak daje ścieżkę szerokości 0,4 mm lub wielokrotność. Próby z cieńszą ścieżką nie wypadły na razie pomyślnie - za słabe krycie i potrafią zostać przetrawione - używam chlorku żelaza.
  22. Jestem, nie wiem czy mogę tak napisać, szczęśliwym, kolejnym posiadaczem klona Arduino Uno i naiwnie wydaje mi się, że już wiem na tyle dużo, że mogę się podzielić moimi przemyśleniami i unikatowymi [moimi] pomysłami i spostrzeżeniami. Jeśli używasz Arduino IDE lokalnie na swoim komputerze, zainteresuj się Arduino Web Edytor'em --> www.arduino.cc / Software / Online tools. --> https://create.arduino.cc Wymienię zalety - aktualna, bieżąca wersja, przechowywanie swoich szkiców w sieci, z dostępem z dowolnego miejsca, przykłady otwierają się w kilku zakładkach [jeżeli są tak przygotowane], między innymi jest schemat ..... Jeżeli jeszcze nie używasz żadnego innego, zainteresuj się koniecznie [i natychmiast] programem fritzing -->http://fritzing.org/home/ Czy chciał[a]byś dowiedzieć się więcej o debounce, podłączaniu przycisku i diody? Więcej niż z kursów czy innych źródeł? A co z dokładnością odmierzania czasu twojego Arduino i czy można z tym coś zrobić? Ja nie znalazłem odpowiedzi na te pytania ani w kursach ani necie i sam doszedłem do pewnych przemyśleń. Może te przemyślenia są tylko dla mnie unikatowe, a dla doświadczonego użytkownika Arduino są to oczywiste sprawy. Oprócz Arduino IDE, jest dużo innych: https://playground.arduino.cc/Main/DevelopmentTools, a i to nie wszystko, https://atom.io, a i do samego Arduino IDE można "przyczepić" inny, zewnętrzny edytor.
  23. WSTĘP Mój projekt składa się z dwóch współpracujących układów działających dzięki mikrokontrolerowi Arduino Uno: pojazdu oraz pilota do sterowania. Pojazd posiada silnik DC napędzający wał z tylnymi kołami oraz serwomechanizm odpowiadający za skręcanie. Urządzenie jest wyposażone w akumulator li-pol zasilający mikrokontroler i silniki. Pilot powstał z użyciem modułu joysticka, który umożliwia analogowe sterowanie prędkością i kierunkiem jazdy pojazdu. Urządzenia komunikują się z wykorzystaniem podczerwieni, pilot wyposażony jest w nadajnik IR, a pojazd w odbiornik IR. MECHANIKA Mechaniczna konstrukcja pojazdu została wykonana z plastikowej butelki. Skrętne koło zostało wymontowane z starego krzesła obrotowego. Układ pilota znajduje się w obudowie z odpowiednio wyciętego kartonowego pudełka. ELEKTRONIKA Oba układy są zlutowane na uniwersalnych płytkach. Płytki zostały wyposażone w listwę goldpinów, które wtyka się w Arduino. Dzięki temu rozwiązaniu możemy wyciągnąć Arduino i użyć go w innym projekcie bez potrzeby niszczenia trwałych połączeń. Pojazd jest wyposażony w jednokanałowy sterownik silnika. Schemat układu pojazdu: Schemat układu pilota: Schematy połączeń stworzono z użyciem programu do projektowania i symulowania obwodów elektronicznych EasyEDA. OPROGRAMOWANIE Najważniejsze fragmenty kodu pilota: valueX = analogRead(joyX) / 100; valueY = analogRead(joyY) / 100; isPress = !digitalRead(button); data0 = String(isPress, HEX); data1 = String(valueX, HEX); data2 = String(valueY, HEX); if (isPress == 0) dataSend += "1"; else dataSend += "2"; dataSend += data1 + data2; for (int i = 0; i < 3; i++) { irsend.sendSony(StrToHex(dataSend.c_str()), 12); delay(40); } Program odczytuje pozycję X oraz Y joysticka oraz czy jest on wciśnięty. Następnie pilot wysyła komunikaty zapisane w systemie szesnastkowym. Są to trzy cyfry: pierwsza stan przycisku (0x1 nie wciśnięty, 0x2 wciśnięty), druga kąt ustawienia serwomechanizmu (od 0x0 do 0xA, 0x0 to ustawienie serwomechanizmu maksymalnie w lewo, 0x5 ustawienie serwomechanizmu prosto, 0xA ustawienie serwomechanizmu maksymalnie w prawo) i trzecia prędkość obrotów silnika (od 0x0 do 0xA, 0x0 to maksymalne obroty do tyłu, 0x5 silnik nie pracuje, 0xA maksymalne obroty do przodu). Przykładowy sygnał 0x171 oznacza serwomechanizm ustawiony delikatnie w prawo oraz prawie maksymalna prędkość w tył. Wszystkie dłuższe sygnały są ignorowane, dzięki czemu ograniczony zostaje wpływ zakłóceń na układ. Stan przycisku nie jest używany przez pojazd ale nic nie stoi na przeszkodzie, aby rozbudować projekt o nową funkcjonalność, jak na przykład zapalanie świateł czy sygnał dźwiękowy. Najważniejsze fragmenty kodu pojazdu: if (irrecv.decode(&results)) { IRcommand = String(results.value, HEX); if (IRcommand.length() == 3) { tmp = IRcommand.charAt(1); setTurn(hexChar2Int(tmp)); tmp = IRcommand.charAt(2); setSpeed(hexChar2Int(tmp)); } irrecv.resume(); } Program używa drugiego i trzeciego znaku do ustawienia kolejno odpowiedniej prędkości oraz kąta obrotu serwomechanizmu. Oba programy wykorzystują bibliotekę <IRremote.h> do komunikacji z użyciem podczerwieni. Dodatkowo program pojazdu korzysta z biblioteki <Servo.h>. Program użyty do napisania programowania mikrokontrolerów to Arduino Software.
  24. Witam Buduję robota na kołach. W jaki sposób rozwiązać zmianę prędkości silników ? Myślałem żeby zrobić kilka przycisków w aplikacji, wybranie powodowałoby ustawienie danej prędkości.
  25. yersey

    DS18B20 dziwne odczyty

    Wszystko działa dobrze, ale gdy temperatura spadnie poniżej 0 to pokazuje jakiś dziwny odczyt. Siedzę nad tym już drugi dzień i nie wiem o co chodzi. Używam arduino nano. Z góry dziękuję za pomoc #include <ModbusRtu.h> #include <OneWire.h> #include <DallasTemperature.h> #include <Wire.h> #include <LiquidCrystal_I2C.h> #define SLAVE_ID 1 #define BUTTON_PIN 7 #define RS_PIN 5 #define DS_PIN 10 #define SUN_PIN A1 #define TEMP_REG 0 #define SUN_REG 1 uint16_t au16data[9] = { 9999, 9999, 2, 3, 2018, 11, 16, 8, 7}; Modbus slave(SLAVE_ID, 0, RS_PIN); OneWire oneWire(DS_PIN); DallasTemperature sensors(&oneWire); DeviceAddress ds18b20 = { 0x28, 0x90, 0x63, 0x45, 0x92, 0x06, 0x02, 0xE0 }; //1 //DeviceAddress ds18b20 = { 0x28, 0x68, 0xA6, 0x45, 0x92, 0x03, 0x02, 0x3C }; //2 //DeviceAddress ds18b20 = { 0x28, 0x12, 0xC6, 0x45, 0x92, 0x08, 0x02, 0x59 }; //3 LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); int temperature = 9999; unsigned long getTempTime = 0; unsigned long lastUpdate = 0; unsigned long buttonTime = 0; int buttonHoldTime = 0; byte sun = 0; bool lcdLight = true; void setup() { sensors.begin(); slave.begin( 9600 ); // baud-rate at 9600 lcd.begin(16,2); pinMode(BUTTON_PIN, INPUT); } void loop() { temperature = readTemp(); if(temperature != NULL || temperature < 200) au16data[TEMP_REG] = temperature; sun = readSun(); if(sun != NULL) au16data[SUN_REG] = sun; slave.poll(au16data, 16); if(millis()-lastUpdate >= 1500UL) { lcd.clear(); printTime(); lcd.setCursor(0, 1); printPage0(); lastUpdate = millis(); } if(digitalRead(BUTTON_PIN) == HIGH){ if(buttonTime == 0) buttonTime = millis(); buttonHoldTime += millis()-buttonTime; buttonTime = millis(); } else{ if(buttonHoldTime > 750){ if(lcdLight == true) lcdLight = false; else lcdLight = true; } else if(buttonHoldTime < 500 && buttonHoldTime > 50) ; buttonHoldTime = 0; buttonTime = 0; } if(lcdLight == true) lcd.backlight(); else lcd.noBacklight(); ////////////////////////////////////// } int readTemp(){ if(millis()-getTempTime >= 1000UL){ sensors.requestTemperatures(); getTempTime = millis(); return sensors.getTempC(ds18b20)*10; } else return temperature; } byte readSun(){ return map(analogRead(SUN_PIN), 0, 1023, 0, 100); } void printTime(){ if(au16data[7] < 10) lcd.print(0); lcd.print(au16data[7]); lcd.print(":"); if(au16data[8] < 10) lcd.print(0); lcd.print(au16data[8]); lcd.print(" "); lcd.print(au16data[6]); lcd.print("."); lcd.print(au16data[5]); lcd.print("."); lcd.print(au16data[4]); } void printPage0(){ lcd.print("T:"); lcd.print(au16data[TEMP_REG]/10); lcd.print("."); lcd.print(au16data[TEMP_REG]-au16data[TEMP_REG]/10*10); lcd.print("*C"); lcd.print(" "); lcd.print("SUN:"); lcd.print(au16data[SUN_REG]); lcd.print("%"); }
×