| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- import sensor, image, time, math, socket, network
- from pyb import Pin
- SSID = "EkoSlupek"
- KEY = "1234567890"
- HOST = ""
- PORT = 8080
- threshold_list = [(190, 255)]
- min_temp_in_celsius = 20.0
- max_temp_in_celsius = 42.0
- treshold_temperature = 38.0
- # Korekta offsetu temperatury (dodaj/subtrahuj stopnie aby dopasować do referencyjnego termometru)
- temperature_offset = -2.0 # Przykład: jeśli kamera pokazuje 38°C a Flir 33°C, ustaw -5.0
- pin1 = Pin('P4', Pin.OUT_PP, Pin.PULL_NONE)
- # --- sensor setup ---
- sensor.reset()
- sensor.ioctl(sensor.IOCTL_LEPTON_SET_MODE, True, False)
- sensor.ioctl(sensor.IOCTL_LEPTON_SET_RANGE, min_temp_in_celsius, max_temp_in_celsius)
- sensor.set_pixformat(sensor.GRAYSCALE)
- sensor.set_framesize(sensor.QQVGA)
- sensor.skip_frames(time=2000)
- # --- WiFi jako Access Point ---
- try:
- wlan = network.WLAN(network.AP_IF)
- wlan.config(ssid=SSID, key=KEY, channel=2)
- wlan.active(True)
- print("AP started: {} IP: {}".format(SSID, wlan.ifconfig()[0]))
- except Exception as e:
- print("WiFi error:", e)
- wlan = None
- def map_g_to_temp(g):
- temp = ((g * (max_temp_in_celsius - min_temp_in_celsius)) / 255.0) + min_temp_in_celsius
- return temp + temperature_offset # Dodaj korekcję offsetu
- def temp_to_g(temp):
- """Konwertuj temperaturę na wartość grayscale"""
- return int(((temp - min_temp_in_celsius) / (max_temp_in_celsius - min_temp_in_celsius)) * 255.0)
- # Próg temperatury - pokazuj tylko bloby powyżej tej temperatury
- min_display_temp = 35.0
- min_display_g = temp_to_g(min_display_temp) # wartość grayscale dla 35°C
- # Próg dla liczenia średniej - liczymy średnią tylko z pikseli powyżej tej temperatury
- # To eliminuje problem z chłodnym otoczeniem w prostokącie bloba
- mean_threshold_temp = 36.5
- mean_threshold_g = temp_to_g(mean_threshold_temp) # wartość grayscale dla 36.5°C
- # Opcja: użyj maksimum zamiast średniej (bardziej wrażliwe na gorączkę, ale może być podatne na zakłócenia)
- # False = użyj średniej z gorących pikseli (ZALECANE)
- # True = użyj maksimum (najgorętszy piksel w blobie)
- use_max_instead_of_mean = False
- # --- Funkcja restartująca serwer (TYLKO socket, bez restartu AP) ---
- def restart_server():
- global server
- try:
- # close old server if exists
- try:
- if server:
- server.close()
- except:
- pass
- # create new server socket
- server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
- server.bind([HOST, PORT])
- server.listen(1) # jedno połączenie naraz
- server.setblocking(False)
- print("Server ready on port", PORT)
- except Exception as e:
- print("Server restart error:", e)
- server = None
- # --- inicjalizacja ---
- server = None
- client = None
- last_client_activity = time.ticks_ms() # aktualizacja przy każdym udanym send/accept
- restart_cooldown = time.ticks_ms() # do ograniczenia częstotliwości restartów
- clock = time.clock()
- # Czas wykrycia gorączki - dla redukcji zakłóceń (opóźnienie 1 sekunda)
- fever_start_time = None
- fever_detection_delay_ms = 1000 # Czas opóźnienia w milisekundach (1 sekunda)
- restart_server()
- while True:
- clock.tick()
- img = sensor.snapshot()
- # ---- analiza temperatury ----
- blob_stats = []
- blobs = img.find_blobs(threshold_list, pixels_threshold=200, area_threshold=200, merge=True)
- for blob in blobs:
- # Liczymy średnią tylko z pikseli powyżej 36.5°C w obszarze bloba
- # To eliminuje problem z chłodnym otoczeniem w prostokącie bloba
- hot_threshold = [(mean_threshold_g, 255)]
- stats = img.get_statistics(thresholds=hot_threshold, roi=blob.rect())
- # Sprawdź czy są jakieś piksele powyżej 36.5°C
- if stats.mean() >= mean_threshold_g:
- if use_max_instead_of_mean:
- # Użyj maksimum (najgorętszy piksel) - bardziej wrażliwe
- temp_g = stats.max()
- else:
- # Użyj średniej z gorących pikseli - bardziej stabilne (ZALECANE)
- temp_g = stats.mean()
- temp = map_g_to_temp(temp_g)
- # Pokazuj tylko bloby z temperaturą > 35°C
- if temp > min_display_temp:
- blob_stats.append((blob.x(), blob.y(), temp, blob))
- img.to_rainbow(color_palette=image.PALETTE_IRONBOW)
- # Sprawdź czy którykolwiek blob ma przekroczoną temperaturę
- has_fever = False
- # Rysuj tylko bloby które mają mean > 35°C
- for blob_stat in blob_stats:
- blob = blob_stat[3] # ostatni element to obiekt blob
- # Rysuj elipsę zamiast prostokąta - lepiej pasuje do kształtu
- try:
- img.draw_ellipse(blob.cx(), blob.cy(), int(blob.w()//2), int(blob.h()//2), 0, color=(0,255,0))
- except:
- # Fallback: prostokąt jeśli elipsa nie działa
- img.draw_rectangle(blob.rect(), color=(0,255,0))
- img.draw_cross(blob.cx(), blob.cy(), color=(0,255,0))
- img.draw_string(blob_stat[0]+2, blob_stat[1]+1, "%.2fC" % blob_stat[2], mono_space=False, color=(0,255,0))
- if blob_stat[2] > treshold_temperature:
- has_fever = True
- # Sterowanie pinem z opóźnieniem 1 sekunda (redukcja zakłóceń)
- current_time = time.ticks_ms()
- if has_fever:
- # Gorączka wykryta - sprawdź czy jest wykrywana wystarczająco długo
- if fever_start_time is None:
- # Pierwsze wykrycie - zapisz czas
- fever_start_time = current_time
- else:
- # Sprawdź czy minęło wystarczająco dużo czasu (>1 sekunda)
- if time.ticks_diff(current_time, fever_start_time) >= fever_detection_delay_ms:
- # Gorączka wykrywana dłużej niż 1 sekunda - włącz pin
- pin1.value(1)
- # Jeśli jeszcze nie minęła sekunda, pin pozostaje w poprzednim stanie
- else:
- # Brak gorączki - resetuj timer i wyłącz pin
- fever_start_time = None
- pin1.value(0)
- # ---- obsługa WiFi: jeśli interfejs się zawiesi -> wypisz i spróbuj ponownie (ale nie zamykamy AP bez potrzeby) ----
- if wlan and not wlan.active():
- print("WiFi down — trying to re-enable AP...")
- try:
- wlan.active(False)
- time.sleep_ms(500)
- wlan.active(True)
- wlan.config(ssid=SSID, key=KEY, channel=2)
- print("AP restarted: {} IP: {}".format(SSID, wlan.ifconfig()[0]))
- # po restarcie interfejsu trzeba odtworzyć server socket
- restart_server()
- except Exception as e:
- print("WiFi restart error:", e)
- # przejdź dalej w pętli
- continue
- # ---- obsługa połączeń (server socket) ----
- if server:
- if not client:
- # spróbuj acceptować (non-blocking)
- try:
- client, addr = server.accept()
- print("New client:", addr)
- client.settimeout(0.1)
- client.send(
- "HTTP/1.1 200 OK\r\n"
- "Server: OpenMV\r\n"
- "Content-Type: multipart/x-mixed-replace;boundary=openmv\r\n\r\n"
- )
- # zaktualizuj timestamp aktywności przy udanym połączeniu
- last_client_activity = time.ticks_ms()
- except OSError:
- # brak nowego klienta — nic nie rób
- client = None
- elif client:
- # jeśli mamy klienta, wysyłaj ramki
- try:
- cframe = img.to_jpeg(quality=70, copy=True)
- header = "\r\n--openmv\r\nContent-Type: image/jpeg\r\nContent-Length:{}\r\n\r\n".format(cframe.size())
- client.send(header)
- client.send(cframe)
- # update ostatniej udanej wysyłki
- last_client_activity = time.ticks_ms()
- except OSError as e:
- # klient rozłączył się lub socket padł -> zamykamy klienta i odtwarzamy server socket
- print("⚠️ Client disconnected or socket error:", e)
- try:
- client.close()
- except:
- pass
- client = None
- # restartujemy TYLKO server socket (workaround WINC bug)
- if time.ticks_diff(time.ticks_ms(), restart_cooldown) > 2000:
- restart_cooldown = time.ticks_ms()
- print("♻️ Recreating server socket (socket-only restart)...")
- try:
- if server:
- try:
- server.close()
- except:
- pass
- restart_server()
- except Exception as e2:
- print("Error recreating server socket:", e2)
- # krótka pauza, żeby nie próbować od razu accept w tej samej iteracji
- time.sleep_ms(200)
- # ---- watchdog: jeśli przez >10 s brak udanej wysyłki/aktywności -> odtwórz server socket (bez restartu AP) ----
- if time.ticks_diff(time.ticks_ms(), last_client_activity) > 10000:
- # ochronna blokada przed zbyt częstymi restartami
- if time.ticks_diff(time.ticks_ms(), restart_cooldown) > 2000:
- print("⏰ No client activity for >10s — recreating server socket (watchdog).")
- try:
- if server:
- try:
- server.close()
- except:
- pass
- restart_server()
- except Exception as e:
- print("Watchdog server recreate error:", e)
- client = None
- last_client_activity = time.ticks_ms()
- restart_cooldown = time.ticks_ms()
- time.sleep_ms(200)
|