Skocz do zawartości

rziomber

Użytkownicy
  • Zawartość

    30
  • Rejestracja

  • Ostatnio

Reputacja

29 Bardzo dobra

O rziomber

  • Ranga
    3/10

Ostatnio na profilu byli

Blok z ostatnio odwiedzającymi jest wyłączony i nie jest wyświetlany innym użytkownikom.

  1. rziomber

    ESP8266 - totalne podstawy

    Jak zaprogramować w Arduino IDE. Na początku przygody najwięcej nauczyłem się z dołączonych przykładów. Chyba najbardziej pomocny był SimpleAuthentification z ESP8266WebServer.
  2. rziomber

    Dlaczego drucik się nie przpala?

    Pewnie ogniwo ma duży opór wewnętrzny i przez drut przepływa przez to niewielkie natężenie prądu.
  3. rziomber

    Raspbery Pi, SPI

    Nie znam się, więc się wypowiem (nigdy nie używałem SPI pod Raspberry). Poszukaj "Wiring PI SPI". http://wiringpi.com/reference/spi-library/ https://learn.sparkfun.com/tutorials/raspberry-pi-spi-and-i2c-tutorial/spi-on-pi
  4. Jeżeli znasz podstawy HTML i JavaScript - polecam Apache Cordova. Napiszesz aplikację tak, jakbyś tworzył normalną stronę internetową. Przydatne będą pluginy: cordova-plugin-background-mode Cordova (PhoneGap) Plugin for Serial Communication over Bluetooth (ale raczej wyśle notyfikację, nie zmaksymalizuje się) Utworzenie nowego projektu (komende wydaj w folderze, w ktorym chcesz go przechowywać): cordova create DualSlider com.dual.slider DualSlider (jako parametry podajesz nazwę aplikacji) Dodanie obsługiwanych platform: cordova platform add android (by aplikacja działała na starych Androidach 4.*): cordova platform add android@6.3.0 Edytujesz plik www/index.html "Kompilacja": cordova build
  5. rziomber

    Arduino + aplikacja MIT app inventor - program

    Być może Twoja aplikacja wysyła "nieodpowiednie" znaki końca linii (\r\n zamiast \n zadeklarowanego w Arduino) albo nie wysyła ich wcale. Wybacz za post nie na temat. Nie znam MIT app inventor. Jeżeli masz podstawy HTML + JavaScript, bardzo polecam Apache Cordova + plugin Cordova (PhoneGap) Plugin for Serial Communication over Bluetooth. Bardzo łatwo stworzysz w tym aplikację mobilną do sterowania Arduino przez Bluetooth. Praktycznie tak, jakbyś pisał normalną stronę internetową. Na dodatek to zwykłe programowanie, więc uzyskasz chyba większą kontrolę nad tym co robisz względem "układania klocków". Przykład programu: Wyzwalacz Bluetooth z interwałometrem do lustrzanki cyfrowej Canon EOS.
  6. rziomber

    Przenoszenie projektu, bibliotek arduino

    Swoją drogą polecam nawyk notowania adresów użytych bibliotek. #include <RCSwitch.h> //https://github.com/sui77/rc-switch
  7. rziomber

    Sterowanie silniczkami 24v DC poprzez Raspberry Pi

    Jeżeli silniki mają kręcić sie tylko w jednym kierunku - pewnie wystarczy MOSFET sterowany przez transoptor z pinu Raspberry. Nie zapomnij w takim wypadku wstawić rezystor ok 10k między Gate a Source tranzystora. Wylicz odpowiednią wartość rezystora pomiędzy pinem Raspberry a transoptorem. Jeśli silnik ma też zawracać - spróbuj mostka-H. QT pewnie dogada się z biblioteką WiringPI. Poszukaj hasła: PWM Wiring Pi
  8. 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); }
  9. 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
  10. 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
  11. rziomber

    Sejf z wykorzystaniem Arduino

    TTP229 z magistralą I2C zastępuje klawiaturę z miliardem kabli Swoją drogą ciekawe byłoby sporządzenie takiego modelu pod kątem zabaw w power analysis.
  12. Współczesny sprzęt astronomiczny pozwala na znaczną automatyzację. Możemy więc połączyć się przez sieć z komputerem podłączonym do montażu (pulpit zdalny Windows, VNC czy linuksowy SSH). Często rolę komputera w obserwatorium spełnia Raspberry Pi, które świetnie współpracuje z pakietem INDI/EKOS. Możemy więc rozpocząć wielogodzinne zbieranie materiału i pójść spać. Montaż automatycznie podążał będzie za ruchem dobowym nieba, a kamera CCD lub CMOS zarejestruje materiał, który potem można stackować. Niestety niektóre konfiguracje sprzętu grożą zderzeniem tuby teleskopu ze statywem lub elementami montażu paralaktycznego (np z wystającym silnikiem) Nie powinno się więc zostawiać "samopas" włączonego teleskopu na dłuższy czas, gdyż ruch dobowy nieba może doprowadzić do "zakneblowania" mechanizmu. Stąd też pomysł sterownika zasilania, który po skończonej sesji wyłączy za nas napęd. Powinien on "rozmawiać" z portem USB komputera obserwatoryjnego, by jego działanie moglibyśmy zaprogramować zdalnie. Kolega Michał Mizera poprosił mnie o rozbudowę Jego sterownika zasilania o taką funkcjonalność. Pudełko posiada w sobie również Raspberry Pi sterujące teleskopem i sterownik grzałek odraszających. Mocowane jest bezpośrednio do montażu za pomocą rzepu. Zdjęcia za zgodą Autora: Po wgraniu do Arduino Nano szkicu poniżej i podłączeniu do niego przekaźnika będziemy mogli sterować urządzeniem z terminala szeregowego. on / off - włącz / wyłącz teraz off - wyłącz za HH:MM:SS reset - zresetuj terminarz protectbutton / protectbuttonoff - programowe "rozbrojenie" przycisku do ręcznego sterowania, by przez przypadek niezamierzenie nie odciąć zasilania #define RelayPin 13 #define LEDPin 11 #define ButtonPin 2 #define RelayOnLowHigh 1 // set 0 if relay is switched on during LOW level #define LEDbrightness 5 // PWM 0-255 String receivedData = "", command = "", receivedCommand = ""; unsigned long eventTime = 0; unsigned long lastButtonchange = 0; unsigned long serialTimer = 0; bool powerStatus; bool protectButton = 0; void changeStatus(); void button(); String getValue(String, char, int); void setup() { Serial.begin(9600); pinMode(RelayPin, OUTPUT); digitalWrite(RelayPin, LOW); pinMode(LEDPin, OUTPUT); digitalWrite(LEDPin, LOW); pinMode(ButtonPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(ButtonPin), button, FALLING); } void loop() { bool status; String timeString = ""; int i; receivedData = Serial.readStringUntil('\n'); receivedData.toLowerCase(); if (eventTime && millis() >= eventTime) { receivedData = command; } if (receivedData == "on") { powerStatus = 1; changeStatus(); } if (receivedData == "off") { powerStatus = 0; changeStatus(); } if (receivedData == "reset") { eventTime = 0; command = ""; } if (receivedData == "protectbutton") { protectButton = 1; } if (receivedData == "protectbuttonoff") { protectButton = 0; } for (i = 0; (receivedData[i] < 47 || receivedData[i] > 58) && i < receivedData.length(); i++) { receivedCommand += receivedData[i]; } if (receivedCommand == receivedData || (receivedCommand != "on" && receivedCommand != "off")) { receivedCommand = ""; } if (receivedCommand.length()) { command = receivedCommand; } for (int ii = i; ii < receivedData.length(); ii++) { timeString += receivedData[ii]; } if (timeString.length() && !eventTime && (command == "on" || command == "off") ) { String hours = getValue(timeString, ':', 0); String minuts = getValue(timeString, ':', 1); String seconds = getValue(timeString, ':', 2); eventTime = millis() + 1000 * (3600 * hours.toInt() + 60 * minuts.toInt() + seconds.toInt()); } if (millis() - serialTimer >= 10000) { Serial.print("Power "); powerStatus ? Serial.print("ON.") : Serial.print("OFF."); if (eventTime) { Serial.print(" Turn "); Serial.print(command); Serial.print(" after "); unsigned long lefttime = eventTime - millis(); unsigned long lefttimeh = lefttime / 3600000; lefttime = lefttime - lefttimeh * 3600000; Serial.print(lefttimeh); Serial.print("h "); unsigned long lefttimem = lefttime / 60000; lefttime = lefttime - lefttimem * 60000; Serial.print(lefttimem); Serial.print("m "); unsigned long lefttimes = lefttime / 1000; Serial.print(lefttimes); Serial.print("s."); } if (protectButton) { Serial.print(" Button locked."); } Serial.println(); serialTimer = millis(); } } void changeStatus() { if (!powerStatus) { RelayOnLowHigh ? digitalWrite(RelayPin, LOW) : digitalWrite(RelayPin, HIGH); digitalWrite(LEDPin, LOW); Serial.println("Turned OFF right now."); } else { RelayOnLowHigh ? digitalWrite(RelayPin, HIGH) : digitalWrite(RelayPin, LOW); analogWrite(LEDPin, LEDbrightness); //digitalWrite(LEDPin, HIGH); Serial.println("Turned ON right now."); } eventTime = 0; command = ""; } void button() { if ((millis() - lastButtonchange > 50) && protectButton == 0) { if (powerStatus == 0) { powerStatus = 1; changeStatus(); } if (powerStatus == 1 && (millis() - lastButtonchange < 1500)) { powerStatus = 0; changeStatus(); } } lastButtonchange = millis(); } /* void button() { if ((millis() - lastButtonchange > 500) && protectButton == 0) { powerStatus = !powerStatus; changeStatus(); } lastButtonchange = millis(); } */ // http://arduino.stackexchange.com/questions/1013/how-do-i-split-an-incoming-string String getValue(String data, char separator, int index) { int maxIndex = data.length() - 1; int j = 0; String chunkVal = ""; for (int i = 0; i <= maxIndex && j <= index; i++) { chunkVal.concat(data[i]); if (data[i] == separator) { j++; if (j > index) { chunkVal.trim(); return chunkVal; } chunkVal = ""; } else if ((i == maxIndex) && (j < index)) { chunkVal = ""; return chunkVal; } } } Dla wygody użytkowników niekoniecznie "zaprzyjaźnionych" z wpisywaniem komend dla portu szeregowego powstał program konsolowy. Wykorzystuje on bibliotekę RS-232 for Linux, FreeBSD and windows. Później w środowisku QT C++ dodałem interfejs graficzny. QT to bardzo dobre środowisko do rozpoczęcia przygody ze sterowaniem elektroniką za pomocą komputera. bezpłatne pod warunkiem wydania naszego programu na licencji (L)GPL dobra dokumentacja oraz mnogość dostępnych zasobów w sieci oprócz GUI QT zawiera gotowe narzędzia m.in. do obsługi portu szeregowego, sieci, multimediów, w pewnym stopniu zastępuje Boost przenośność kodu - ten sam kod skompilujemy na Windows, MacOS, Linux, smartfony. QT Creator dostępny jest też w repozytorium Raspbiana, dzięki czemu uruchomimy nasz program na Raspberry Pi bez konieczności w tym wypadku bardzo niewygodnej cross kompilacji. Możemy więc napisać i przetestować kod na komputerze, a potem przenieść go do IDE uruchomionego na Malinie. Do instalacji w systemie Raspbian wystarczy sudo apt-get update sudo apt-get upgrade sudo apt-get install qtcreator gcc qtbase5-dev GUI "wyklikamy" przenosząc gotowe elementy z zasobnika na okno naszego programu. "Magiczne własciwości" z kodu źródłowego C++ skorelujemy z poszczególnymi komponentami graficznymi po prawokliku: Kod źródłowy programów można znaleźć na stronie projektu.
  13. Multimetry logujące dane na komputerze są drogie. Nie mówiąc już o miernikach monitorujących równocześnie napięcie, natężenie i moc oraz rezystancję odbiornika. Może więc warto zrobić coś takiego samemu? Projekt służy jedynie do pomiaru w obwodach prądu stałego przy NIEWIELKICH napięciach (nominalnie do 26V) i ograniczonym natężeniu (sprawdź specyfikację zastosowanego modułu). Absolutnie nie należy go stosować przy napięciu sieciowym, czy też wysokowydajnych źródłach prądu (np akumulator samochodowy pomimo bezpiecznego napiącia 13V w razie zwarcia może dostarczyć KILKASET AMPERÓW, co również może okazać się groźne)! Składniki: Arduino Nano (wbudowane gniazdo Micro USB zapewni łatwe zasilanie z powerbanku, możemy też użyć Pro Mini, które zapewni większą energooszczędność) moduł miernika z magistralą I²C INA219 wyświetlacz LCD 2x16 z konwerterem I²C LCM1602 Bluetooth - UART np w postaci modułu HC-06 czy HC-05 bezpiecznik (zastosowałem PPTC 3A) gniazdo oraz wtyki bananowe 4 mm gniazdo DC 5.5/2.1 mm- Bluetooth HC-05 oraz 06 obsługują poziom logiczny napięć 3.3V. Gotowe moduły posiadają jednak na ogół stabilizator pozwalający zasilić je bezpośrednio z 5V. Nadal pozostaje jednak problem 5V "logicznych" z wyjścia UART (czyli pinu TX) w Arduino. Najlepiej zastosować tu dzielnik napięcia lub konwerter poziomów logicznych. Ja wstawiłem szeregowo rezystor by w razie czego ograniczyć natężenie prądu. To jeszcze nie "zabiło" HC-06, ale też nie jest rozwiązaniem "podręcznikowym" i zalecanym. Port nadawczy (TX) w Bluetooth możemy za to bez obaw podłączyć bezpośrednio z odbiorczym (RX) w Arduino. Kod źródłowy w tym wypadku był prawdę mówiąc banalny do napisania, gdyż wymagał jedynie drobnych zmian przykładu dla stosowanej biblioteki. #include <Wire.h> #include <Adafruit_INA219.h> //https://github.com/adafruit/Adafruit_INA219 #include <LiquidCrystal_I2C.h> //https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library #include <Narcoleptic.h> //https://github.com/rcook/narcoleptic #define DiodeVoltageDrop 0.42 LiquidCrystal_I2C lcd(0x27, 16, 2); Adafruit_INA219 ina219; void setup(void) { Serial.begin(9600); while (!Serial) { // will pause Zero, Leonardo, etc until serial console opens delay(1); } Narcoleptic.disableTimer1(); Narcoleptic.disableTimer2(); Narcoleptic.disableADC(); Narcoleptic.disableSPI(); uint32_t currentFrequency; // Initialize the INA219. // By default the initialization will use the largest range (32V, 2A). However // you can call a setCalibration function to change this range (see comments). ina219.begin(); // To use a slightly lower 32V, 1A range (higher precision on amps): //ina219.setCalibration_32V_1A(); // Or to use a lower 16V, 400mA range (higher precision on volts and amps): //ina219.setCalibration_16V_400mA(); lcd.begin(); lcd.backlight(); } void loop(void) { // float shuntvoltage = 0; float busvoltage = 0; float current_mA = 0; // float loadvoltage = 0; float power_mW = 0; // shuntvoltage = ina219.getShuntVoltage_mV(); busvoltage = ina219.getBusVoltage_V() + DiodeVoltageDrop; current_mA = 1086.5 / 1104.0 * (ina219.getCurrent_mA() - 1.4); power_mW = abs(busvoltage * current_mA); float impedance = abs(busvoltage / current_mA * 1000.0); // power_mW = ina219.getPower_mW(); // loadvoltage = busvoltage + (shuntvoltage / 1000); Serial.print(busvoltage); Serial.print("V "); Serial.print(current_mA, 1); Serial.print("mA "); Serial.print(power_mW, 0); Serial.print("mW "); Serial.print(impedance, 1); Serial.println((char)244); lcd.clear(); lcd.print(busvoltage); lcd.print("V "); lcd.print(current_mA, 1); lcd.print("mA"); lcd.setCursor(0, 1); lcd.print(power_mW, 0); lcd.print("mW "); lcd.print(impedance, 1); lcd.print((char)244); Narcoleptic.delay(500); } Do pomiaru napięcia przez układ INA219 wymagane jest połączenie GND zasilania elektroniki z GND mierzonego obwodu. Z tego też względu nie zalecam zasilać urządzenia z gniazda USB w komputerze. W razie pomyłki możemy uszkodzić komputer! Dlatego do "kablowania" danych użyłem Bluetooth. INA219 niestety nie toleruje również odwrotnej polaryzacji - potencjał po stronie mierzonego prądu nie może być niższy względem GND. Może to doprowadzić do uszkodzenia układu. Nie jest to więc urządzenie odpowiednie do "ręcznego" pomiaru napięcia zwykłymi sondami z multimetru - niezmiernie łatwo tu o odwrotną polaryzację! Dla bezpieczeństwa wstawiłem diodę pomiędzy masą miernika a mierzonego układu. Nieco przekłamuje to pomiar napięcia (spadek napięcia na diodzie nie jest dokładnie stały, jak skorygowałem w szkicu). Za to sama konstrukcja powinna zapewnić wygodną obsługę. Z boku znajdziemy gniazdo DC do podłączenia zasilacza dla mierzonego układu oraz gniazda bananowe dla kabli "wyjściowych". Do zestawu oczywiście NIE dołączono uniwersalnego zestawu kabli pomiarowych. Musiałem więc zrobić je sobie sam Przewody z wtykiem bananowym 4 mm na wejściu są zakończone: wtykiem DC 5.5/2.1 mm (wtedy multimetr jest po prostu "przedłużaczem" zwykłego zasilacza) ponownie wtykiem bananowym, co umożliwia zastosowanie np "krokodylków" jako nakładki czy podłączenie "normalnego" multimetru celem kalibracji zaciskiem typu "hak" do bezpośredniego wpinania się w goldpiny Łatwo więc podłączymy nasze urządzenie pomiędzy zasilacz a odbiornik. Po podłączeniu komputera z modułem Bluetooth - UART utworzony jest wirtualny port szeregowy. Np dla Linuksa powstanie plik urządzenia /dev/rfcomm*. Uzyskane pomiary możemy zapisać w postaci logu np za pomocą putty czy też terminalowych narzędzi dla Linuksa. Napisałem też własny program w C++, który zapisze dane otrzymane z portu szeregowego do pliku. Skompilujemy go poprzez g++ serial2log.cpp serialib/serialib.cpp -o serial2log -std=c++17 Wcześniej należy jednak umieścić Serialib ::: Simple Serial Library w folderze serialib. Jako parametry wywoływanego programu podajemy adres portu szeregowego, baud rate oraz nazwę pliku tekstowego z logiem. Np: ./serial2log /dev/ttyUSB0 9600 log.txt Pracę kończymy w bardzo "brutalny" sposób: Ctrl + C. //g++ serial2log.cpp serialib/serialib.cpp -o serial2log -std=c++17 #include <iostream> #include <chrono> #include <thread> #include <fstream> #include <ctime> #include "serialib/serialib.h" //http://serialib.free.fr int main(int argc, char *argv[]) { if (argc != 4) { std::cout << "Usage: ./serial2log serial_port baud_rate log_file\n"; return 0; } serialib LS; // Object of the serialib class int Ret; // Used for return values char Buffer[128]; // Open serial port Ret=LS.Open(argv[1],atoi(argv[2])); // Open serial link if (Ret!=1) { // If an error occured... std::cout << "Error while opening port. Permission problem?\n"; // ... display a message ... return Ret; // ... quit the application } std::cout << "Serial port opened successfully!\n"; std::ofstream logfile; logfile.open(argv[3], std::ios::out | std::ios::app); /* Ret=LS.WriteString("AT\n"); // Send the command on the serial port if (Ret!=1) { // If the writting operation failed ... std::cout << "Error while writing data\n"; // ... display a message ... return Ret; // ... quit the application. } std::cout << "Write operation is successful \n"; */ while(1){ Ret=LS.ReadString(Buffer,'\n',128,5000); // Read a maximum of 128 characters with a timeout of 5 seconds // The final character of the string must be a line feed ('\n') if (Ret>0){ std::time_t currentTime = std::time(nullptr); std::cout << currentTime << " >> " << Buffer; // If a string has been read from, print the string logfile << currentTime << " >> " << Buffer << std::flush; } else std::cout << "TimeOut reached. No data received !\n"; // If not, print a message. std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // Close the connection with the device LS.Close(); logfile.close(); return 0; } Zastosowanie: całkowanie numeryczne poboru prądu po czasie. Mamy urządzenie, którego "łakomstwo" zmienia się w czasie, a my chcemy oszacować czas jego pracy na akumulatorze. Musimy więc jakoś uśrednić jego pobór prądu. Np montaż paralaktyczny teleskopu pobiera inne natężenie prądu w trakcie podążania za ruchem dobowym nieba, a inne w trybie pozycjonowania na zadany obiekt (GoTo). Po testach będziemy mogli więc oszacować, jak długo będzie on pracował zasilany z akumulatora żelowego.
×