sobota, 11 września 2021

Projekt MotoFocuser do teleskopu Celestron C6-N

Wcześniej pracowałem nad Focuserem do mniejszego teleskopu, ale w międzyczasie kupiłem 6-cio calowego Celestrona, którego wyciąg okularowy jest łatwiejszy do modyfikacji (odkręcane pokrętła i 4 śruby mocujące). Dlatego tym razem poszło łatwo. Miałem już silnik 17HS4023, do sterowania stepstick A4988 i do kontroli ESP32. zastosowałem dwa koła zębate z paskiem (przełożenie 16:40). Dzięki temu nawet powerbank dał radę zasilić wszystko, przez kabel z konwertujący napięcie na 9V. U mnie konkretnie pracuje ESP32-WROOM-32D, ale można zastosować dowolny ESP32 z WiFi, RP2040 lub ESP8266. Ideą było, by można było sterować przez przeglądarkę, przez WebAPI np. z programu w Pythonie lub z pilota na kablu.

Elementy

mikrokontroler: ESP32-WROOM-32D
silnik krokowy: 17HS4023
sterownik silnika krokowego: A4988
zasilanie mikrokontrolera: Moduł Mini Stabilizatora AMS1117 3,3V
klawiatura samoprzylepna 4 przyciski
Kabel USB na wtyk 5,5/2,1 z konwerterem napięcia 5V na 9V
Pasek zębaty 140mm długości 6mm szerokości
Zębatka 16 zębów 5mm do silnika
Zębatka 40 zębów 10mm do pokrętła
Mocowanie stalowe typu L do NEMA 17
płytka uniwersalna
wtyk JST XH2.54, złącze zaciskowe, listwa goldpin

Pewnie coś tu pominąłem, ale w 100-150pln można się zamknąć.


Oprogramowanie Micropython dla ESP32

Najłatwiej zainstalować edytor Thonny. On pozwoli wgrać automatycznie najnowsze oprogramowanie MicroPython. Można też pobrać je samemu z micropython.org. Rozbudowana dokumentacja do MicroPython jest na stronie projektu micropython.org. Edytor Thonny wydał mi się też najłatwiejszy w użyciu. Jeśli pracujesz na Windows będziesz potrzebował sterownika Serial USB. powinien się sam wyszukać po podłączeniu mikrokontrolera do komputera. Przydać się też może ESPTool i zapomniałbym o najważniejszym Python for Windows z python.org.



Ustawienie stepstick-a

Sterownik silnika krokowego A4988 posiada potencjometr i należy nim ustawić napięcie wyjściowe, które wylicza się z wzoru. Sposób został opisany np. tu https://printo3d.pl/regulacja-stepstickow/. Dla mojego silnika i mojego sterownika, powinno to być 0,5-0,6 V.


Testowe podłączenie układu

Próbne podłączenie zrobiłem spinając wszystko przewodami do płytek stykowych.


A4988 PinPołączenie
VMOTBateria +
GNDBateria -
VDD3.3V
GNDGND ESP32
STP26 ESP32
DIR27 ESP32
1A, 1B, 2A, 2BSilnik
MS1, MS2, MS314, 15, 18 ESP32


Schemat i zdjęcie poniżej:

Kod MicroPythona

Sposób sterowania silnikiem podejrzałem na stronie techtotinker.blogspot.com. Sterowanie przez przeglądarkę z hugokernel/micropython-nanoweb. Sposób obsługi klawiatury zaczerpnąłem z peppe8o.com. Obługa socketów z peterhinch/micropython-async.


Tu kod z obsługą keypad-a


# Micropython code for ESP32
# MotoFocuser project for the Celestron C6-N telescope
# Author: Wojtek B.
from machine import Pin
from time import sleep_ms
import uasyncio


class stepperMotor:
    def __init __ (self, stepPin, dirPin):
        self.step = Pin (stepPin, Pin.OUT)
        self.dir = Pin (dirPin, Pin.OUT)
        self.quarter = Pin (15, Pin.OUT)
        self.quarter.value (1)
        self.turn = 'S'
        self.speed = 0
        self.phase = 0
        # self.block = False
        self.queue = []

    def change (self, turn, speed):
        # self.block = True
        # self.turn = turn
        # self.speed = speed
        self.queue.append ([turn, speed])    
        # self.block = False
        print ('New-> turn:', self.turn, 'speed:', self.speed)
        
    
    async def rotate (self):
        r = {'L': 0, 'R': 1}
        print ('Rotate')
        while True:
            # Get a "work item" out of the queue.
            if len (self.queue)> 0: # and self.block == False:
                # self.block = 
                # print ('Q len:', self.queue)
                params = self.queue [len (self.queue) -1]
                self.queue = []
                print ('p:', params)
                # I change the step
                self.turn = params [0]
                self.speed = params [1]
                self.phase = 0
                print ('New-> turn:', self.turn, 'speed:', self.speed)
            # follow the steps according to the data
            if self.turn == 'S' or self.speed == 0:
                # stepper.rotate (0, 'R', 10000)
                # commands to A4988
                # uasyncio.sleep (20)
                await uasyncio.sleep_ms (1)
            else:
                if self.speed == 2 and self.turn in ['R', 'L']:
                    # s = 56 - 5 * self.phase
                    # quick
                    s = 10
                    if self.phase <11:
                        self.phase + = 1
                        print (s)
                    elif self.phase == 12:
                        s = 1
                        
                elif self.speed == 1 and self.turn in ['R', 'L']:
                    # slow
                    s = 100
                else:
                    print ('Err')
                # commands to A4988
                self.dir.value (r [self.turn])
                self.step.value (1)
                await uasyncio.sleep_ms (s)
                self.step.value (0)
                #await uasyncio.sleep_ms (s)



class Keypad:
    
    def __init __ (self):
        self.Pin0 = Pin (15, Pin.IN, Pin.PULL_DOWN) # GND
        self.pinR1 = Pin (2, Pin.OUT) # 3
        self.pinR2 = Pin (0, Pin.OUT) # 4
        self.pinL2 = Pin (4, Pin.OUT) # 1
        self.pinL1 = Pin (16, Pin.OUT) # 2

        self.keys = [self.pinL2, self.pinL1, self.pinR1, self.pinR2]
        self.labels = ['Left <<', 'Left <', '> Right', '>> Right']
        self.turns = ['L', 'L', 'R', 'R']
        self.speeds = [2, 1, 1, 2]
        self.states = [0, 0, 0, 0]

    async def run (self):
        print ('Run')
        while True:
            for i, k in enumerate (self.keys):
                k.value (1)
                # sleep_ms (40)
                check = self.Pin0.value ()
                if check != self.states [i]:
                    print (self.labels [i], '=', check)
                    if check == 1:
                        pass
                        motor.change (self.turns [i], self.speeds [i])
                    if check == 0:
                        motor.change ('S', 0)
                        print ('.')
                    self.states [i] = check
                k.value (0)
                #print ('Run', i)
                await uasyncio.sleep_ms (50)


motor = stepperMotor (stepPin = 26, dirPin = 27)
keypad = Keypad ()

loop = uasyncio.get_event_loop ()
loop.create_task (motor.rotate ())
loop.create_task (keypad.run ())
loop.run_forever ()

Wersja na płytce uniwersalnej

Zastosowałem płytkę uniwersalną z otworami połączonymi po 3 szt. w rastrze 2,54mm THT i listwy goldpin. Gniazdo do silnika JST XH2.54.

Ponieważ zasilam jednym źródłem zasilania, to zastosowałem stabilizator AMS1117. Obsługuje on napięcie wejściowe DC 4,5-12V. Stepstick A4988 przyjmuje napięcie 8-35V. Ja zamierzam zasilać 9V lub 12V z zasilacza sieciowego lub jeśli nie będzie w zasięgu gniazdka sieciowego z powerbaku lub komputera przez kabel USB zawierający konwerter 5V na 9V. Pierwsze testy tego kabelka wyszły pozytywnie. Trzeba tylko pamiętać by podłączając go do powerbaku wpinać go tylko gdy jest używany. Inaczej szybko wyzeruje powerbank.  

Uwaga. Dokumentacja do mojej wersji ESP32 mówi, że można je zasilać tylko jedną drogą, więc jeśli chcę podłączyć układ do komputera po USB, zawsze wyjmuję stabilizator AMS1117. Klawiaturę czteroprzyciskową podłączyłem do pin-ów 2, 0, 4, 16. Służą do obsługi dwóch prędkości w dwóch kierunkach.

 





Proste pudełko

Przygotowałem tymczasowe pudełko. Po prostu zmniejszyłem istniejące tekturowe opakowanie. Widok poniżej:

Montaż na teleskopie

Ponieważ można tu łatwo odkręcić pokrętło wyciągu okularowego, a śruby mocujące mechanizm zębatkowy wykorzystać jako mocowanie silnika, to sposób zamocowania był prosty. Mocowanie stalowe typu L do NEMA 17 przykręcone do wyciągu i dwa koła zębate 16 zębów i 40 zębów połączone paskiem zębatym 14cm rozwiązało problem. trzeba było jedynie wymienić śruby na dłuższe i zadbać by pasek był właściwie naciągnięty i jednocześnie pokrywa obudowy mechanizmu zębatkowego wyciągu była właściwie dokręcona. Mi potrzebną odległość zapewniła nakrętka i podkładka. 

Dzięki przełożeniu 1:2,5 zmniejszyły się wymagania dla zasilania i dlatego mogłem użyć powerbanku z kablem konwertującym napięcie na 9V. 

Zdjęcie poglądowe poniżej:






ToDo

dodać podłączenie do znanego AP WiFi, a jeśli jego brak to wystawienie własnego

dodać serwer ze stroną WWW z obsługą MotoFocusera

dodać obsługę sterowania przez TCP dla aplikacji typu RoboRemoFree  

dopisać WebAPI  dla programu sterowania kamerą Raspberry Pi