├── img ├── README.md ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png ├── 9.png ├── 10.png ├── 11.png ├── 12.png ├── 13.png ├── 14.png ├── 15.png ├── full.jpg ├── AccelDSC.jpg ├── download.png ├── Skysafari_3.png ├── Skysafari_4.png ├── components.jpg ├── connected_1.jpg ├── connected_2.jpg ├── full_close.jpg ├── solderless.jpg ├── AccelCircuit.jpg ├── Alt_encoder_1.jpg ├── Alt_encoder_2.jpg ├── Alt_encoder_3.jpg ├── solderless_lg.jpg ├── webConfig_sm.png ├── 12V_power_supply.jpg ├── Circuit_ESP32S.png ├── Sketch_too_big.png ├── battery_options.jpg ├── pushover_api_key.png ├── pushover_new_app.png ├── WiFi_NoInternet_C.png ├── keyes_screw_shield.jpg └── pushover_user_key.png ├── dobDSC-back.jpg ├── dobDSC-front.jpg ├── dobDSC-upclose.jpg ├── README.md ├── README.es.md ├── DobsonianDSC ├── WebConfig_DSC.h ├── DobsonianDSC.ino └── WebConfig_DSC.cpp ├── docs ├── Solderless.md ├── Solderless.es.md ├── ArduinoIDE.md ├── ArduinoIDE.es.md ├── UploadConfigure.md └── UploadConfigure.es.md ├── AccelDSC └── AccelDSC.ino └── LICENSE /img/README.md: -------------------------------------------------------------------------------- 1 | Images folder 2 | -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/3.png -------------------------------------------------------------------------------- /img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/4.png -------------------------------------------------------------------------------- /img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/5.png -------------------------------------------------------------------------------- /img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/6.png -------------------------------------------------------------------------------- /img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/7.png -------------------------------------------------------------------------------- /img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/8.png -------------------------------------------------------------------------------- /img/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/9.png -------------------------------------------------------------------------------- /img/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/10.png -------------------------------------------------------------------------------- /img/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/11.png -------------------------------------------------------------------------------- /img/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/12.png -------------------------------------------------------------------------------- /img/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/13.png -------------------------------------------------------------------------------- /img/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/14.png -------------------------------------------------------------------------------- /img/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/15.png -------------------------------------------------------------------------------- /img/full.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/full.jpg -------------------------------------------------------------------------------- /dobDSC-back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/dobDSC-back.jpg -------------------------------------------------------------------------------- /dobDSC-front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/dobDSC-front.jpg -------------------------------------------------------------------------------- /img/AccelDSC.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/AccelDSC.jpg -------------------------------------------------------------------------------- /img/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/download.png -------------------------------------------------------------------------------- /dobDSC-upclose.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/dobDSC-upclose.jpg -------------------------------------------------------------------------------- /img/Skysafari_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/Skysafari_3.png -------------------------------------------------------------------------------- /img/Skysafari_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/Skysafari_4.png -------------------------------------------------------------------------------- /img/components.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/components.jpg -------------------------------------------------------------------------------- /img/connected_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/connected_1.jpg -------------------------------------------------------------------------------- /img/connected_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/connected_2.jpg -------------------------------------------------------------------------------- /img/full_close.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/full_close.jpg -------------------------------------------------------------------------------- /img/solderless.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/solderless.jpg -------------------------------------------------------------------------------- /img/AccelCircuit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/AccelCircuit.jpg -------------------------------------------------------------------------------- /img/Alt_encoder_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/Alt_encoder_1.jpg -------------------------------------------------------------------------------- /img/Alt_encoder_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/Alt_encoder_2.jpg -------------------------------------------------------------------------------- /img/Alt_encoder_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/Alt_encoder_3.jpg -------------------------------------------------------------------------------- /img/solderless_lg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/solderless_lg.jpg -------------------------------------------------------------------------------- /img/webConfig_sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/webConfig_sm.png -------------------------------------------------------------------------------- /img/12V_power_supply.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/12V_power_supply.jpg -------------------------------------------------------------------------------- /img/Circuit_ESP32S.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/Circuit_ESP32S.png -------------------------------------------------------------------------------- /img/Sketch_too_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/Sketch_too_big.png -------------------------------------------------------------------------------- /img/battery_options.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/battery_options.jpg -------------------------------------------------------------------------------- /img/pushover_api_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/pushover_api_key.png -------------------------------------------------------------------------------- /img/pushover_new_app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/pushover_new_app.png -------------------------------------------------------------------------------- /img/WiFi_NoInternet_C.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/WiFi_NoInternet_C.png -------------------------------------------------------------------------------- /img/keyes_screw_shield.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/keyes_screw_shield.jpg -------------------------------------------------------------------------------- /img/pushover_user_key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vlaate/DobsonianDSC/HEAD/img/pushover_user_key.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DobsonianDSC 2 | [[English]](https://github.com/vlaate/DobsonianDSC) 3 | [[Español]](https://github.com/vlaate/DobsonianDSC/blob/master/README.es.md) 4 | 5 | Amateur astronomers want to know where their telescope is pointing at. For this reason, many commercial telescopes (like the Orion IntelliScope) come equipped with "push to" features, often based on high precision optical rotary encoders attached to the telescope mount, and a hand control device with a database of coordinates of thousands of stars and other sky objects. 6 | 7 | There are also commercial Digital Setting Circles, which are kits sold for telescope owners to adapt to their existing telescope, in order to give it "push to" features, costing a few hundred dollars. 8 | 9 | The mobile app [SkySafari](https://skysafariastronomy.com/) comes with a celestial database that is often more up-to-date, and it's user interface (a visual representation of the night sky) can be considered superior to what text lines on a push-to hand controller allow. 10 | 11 | This project is an open source implementation of Digital Setting Circles for dobsonian telescopes. By using two inexpensive optical encoders and an ESP32 microcontroller it enables amateur astronomers to connect their dobsonian telescope to the SkySafari app. 12 | 13 | Particular emphasis has been placed on making sure anybody can build the circuit regardless of their experience (or lack thereof) with electronics or arduino, including a step-by-step guide for a very simple implementation that requires no soldering. 14 | 15 | This DSC achieves the following: 16 | 17 | * Absolute minimum number of components, making it simple and inexpensive. (The bill of materials is under **$40**) 18 | * Easy to build, no soldering required. 19 | * Wireless communication with the SkySafari app, No need for a wired handheld control. 20 | * Ability to connect to an external WiFi (to maintain access to the internet while using the DSC) 21 | * Web configuration page for easily configuring everything: WiFi settings/password, encoder resolutions, flipping encoder pins, notification API/keys, device name. 22 | * Bluetooth support. 23 | 24 | This is what it looks like installed on a small tabletop dobsonian: 25 | 26 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/full.jpg "Mini Dob with DSC") 27 | 28 | 29 | ## Documentation 30 | 31 | * This [Arduino IDE guide](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.md) will provide beginner-friendly step by step instructions on how to install and configure the Arduino IDE on your computer, including the board managers and libraries required by the DSC, so that you can upload this software to an ESP-32 microcontroller. 32 | * This [Upload and Configuration guide](https://github.com/vlaate/DobsonianDSC/blob/master/docs/UploadConfigure.md) provides step by step instructions on how to upload the DSC software to the ESP32 microcontroller, how to configure the DSC using it's web configuration interface, and how to connect the SkySafari app to it. 33 | * The [Circuit Building Guide](https://github.com/vlaate/DobsonianDSC/blob/master/docs/Solderless.md) contains the circuit schematics, the parts list and a step-by-step instructions on how to assemble a beginner-friendly **solderless** implementation of the circuit. 34 | * You can ask questions or provide feedback in the [CloudyNights forums](https://www.cloudynights.com/topic/589521-37-dobsonian-dsc-for-diy-makers/). 35 | * Useful information on how to attach altitude encoders to dobsonians can be found [here](https://www.cloudynights.com/topic/772803-how-to-attach-altitude-encoders-to-dobsonians/) 36 | 37 | ## Pictures 38 | 39 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/full_close.jpg "Close up AZ") 40 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/Alt_encoder_1.jpg "Alt Encoder 1") 41 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/Alt_encoder_2.jpg "Alt Encoder 2") 42 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/components.jpg "Components") 43 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/webConfig_sm.png "Web Configuration") 44 | 45 | 46 | -------------------------------------------------------------------------------- /README.es.md: -------------------------------------------------------------------------------- 1 | # Apuntador Digital para Telescopios Dobsonianos (ADT) 2 | [[English]](https://github.com/vlaate/DobsonianDSC) 3 | [[Español]](https://github.com/vlaate/DobsonianDSC/blob/master/README.es.md) 4 | 5 | Los astrónomos aficionados desean saber hacia dónde está apuntando su telescopio. Por ésta razón, algunos telescopios comerciales como el Orion IntelliScope vienen equipados con funcionalidades tipo “push-to”, basadas en encoders ópticos de alta precisión conectados a la montura del telescopio, y un dispositivo electrónico o control de mano que tiene una base de datos con las coordenadas de miles de objetos celestes. 6 | 7 | También existen apps para Android y para iOS como [SkySafari](https://skysafariastronomy.com/), que contienen bases de datos celestes incluso mejores, y que ofrecen una interfaz de usuario muy superior a los controles de mano que vienen con los telescopios push-to. 8 | 9 | Éste proyecto es una implementación de código libre “open source” de un Apuntador Digital para Telescopio, que mediante dos encoders ópticos de bajo costo y un microcontrolador ESP32 permite conectar un telescopio dobsoniano con la app SkySafari, y así poder ver fácilmente hacia dónde está apuntando el telescopio. 10 | 11 | En el diseño del apuntador digital, se puso mucho énfasis en lograr que cualquier astrónomo aficionado pueda construir el circuito incluso si no tiene experiencia alguna con electrónica ni con Arduino. Para ello, el proyecto ofrece guías paso-a-paso ilustradas que indican desde cómo instalar y configurar Arduino hasta como ensamblar de una forma muy sencilla los componentes sin tener que soldar, de manera que incluso personas que no tengan un cautín (o no sepan cómo usarlo) puedan construir el ADT para su telescopio. 12 | 13 | El ADT logra lo siguiente: 14 | 15 | * Un mínimo absoluto de componentes electrónicos, haciéndolo sencillo y de bajo costo (las partes cuestan menos de **USD $40**). 16 | * Fácil de construir, no necesita soldadura ni herramientas especiales. 17 | * Comunicación inalámbrica con apps como SkySafari Plus/Pro. 18 | * Capacidad de conectarse a un WiFi externo/existente (para poder seguir conectado a internet mientras se usa el ADT vía WiFi). 19 | * Soporta conexión Bluetooth. 20 | * Página Web de configuración, para fácilmente ajustar todo: password WiFi, resoluciones de los encoders, reversar dirección de encoders, notificación, nombre del dispositivo. 21 | 22 | Así se ve cuando está instalado en un pequeño dobsoniano de sobremesa: 23 | 24 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/full.jpg "Mini Dob with DSC") 25 | 26 | ## Documentación 27 | 28 | * La [Guía de Arduino para Principiantes](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.es.md) explica de manera ilustrada y paso a paso cómo instalar y configurar Arduino en su computador, incluyendo las placas y librerías necesarias para que pueda cargar el software de éste ADT en un microcontrolador ESP-32. 29 | * La [Guía para Cargar y Configurar el ADT](https://github.com/vlaate/DobsonianDSC/blob/master/docs/UploadConfigure.es.md) provee instrucciones paso a paso para cargar el software ADT en un microcontrolador ESP32 microcontroller, para configurarlo, y para conectarse con la app SkySafari. 30 | * La [Guía de construcción del Circuito](https://github.com/vlaate/DobsonianDSC/blob/master/docs/Solderless.es.md) contiene la lista de partes, diagrama esquemático e instrucciones paso a paso para ensamblar el circuito del ADT de manera sencilla, sin necesitar soldadura ni herramientas especiales. 31 | * Si tiene problemas o comentarios, puede escribirnos en el foro de [CloudyNights](https://www.cloudynights.com/topic/589521-37-dobsonian-dsc-for-diy-makers/). 32 | * [Aquí](https://www.cloudynights.com/topic/772803-how-to-attach-altitude-encoders-to-dobsonians/) puede encontrar información útil sobre cómo montar mecánicamente encoders en algunos tipos de dobsonianos. 33 | 34 | ## Fotos 35 | 36 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/full_close.jpg "Close up AZ") 37 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/Alt_encoder_1.jpg "Alt Encoder 1") 38 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/Alt_encoder_2.jpg "Alt Encoder 2") 39 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/components.jpg "Components") 40 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/webConfig_sm.png "Web Configuration") 41 | -------------------------------------------------------------------------------- /DobsonianDSC/WebConfig_DSC.h: -------------------------------------------------------------------------------- 1 | /* 2 | Original file: WebConfig.h version 1.4 3 | Author Gerald Lechner lechge@gmail.com https://github.com/GerLech 4 | Licensed under the GNU Lesser General Public License v3.0 5 | 6 | This library builds a web page with a smart phone friendly form to edit 7 | a free definable number of configuration parameters. The submitted data will be stored in the SPIFFS 8 | The library works with ESP8266 and ESP32 9 | 10 | It has been modified by Vladimir Atehortúa to customize the HTML styles for 11 | astronomy/night use (dark background with red colors) for the Digital Setting Circles project 12 | which is released under the compatible GPLv3 13 | 14 | This program is free software: you can redistribute it and/or modify 15 | it under the terms of the version 3 GNU General Public License as 16 | published by the Free Software Foundation. 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | You should have received a copy of the GNU General Public License along with this program. 22 | If not, see 23 | */ 24 | 25 | #ifndef WebConfig_h 26 | #define WebConfig_h 27 | 28 | #include 29 | #if defined(ESP32) 30 | #include 31 | #else 32 | #include 33 | #endif 34 | 35 | //maximum number of parameters 36 | #define MAXVALUES 20 37 | 38 | //maximum number of options per parameters 39 | #define MAXOPTIONS 15 40 | 41 | //character limits 42 | #define NAMELENGTH 20 43 | #define LABELLENGTH 40 44 | 45 | //name for the config file 46 | #define CONFFILE "/WebConf.conf" 47 | 48 | #define INPUTTEXT 0 49 | #define INPUTPASSWORD 1 50 | #define INPUTNUMBER 2 51 | #define INPUTDATE 3 52 | #define INPUTTIME 4 53 | #define INPUTRANGE 5 54 | #define INPUTCHECKBOX 6 55 | #define INPUTRADIO 7 56 | #define INPUTSELECT 8 57 | #define INPUTCOLOR 9 58 | #define INPUTFLOAT 10 59 | #define INPUTTEXTAREA 11 60 | #define INPUTMULTICHECK 12 61 | //number of types 62 | #define INPUTTYPES 13 63 | 64 | #define BTN_CONFIG 0 65 | #define BTN_DONE 1 66 | #define BTN_CANCEL 2 67 | #define BTN_DELETE 4 68 | //data structure to hold the parameter Description 69 | typedef //Struktur eines Datenpakets 70 | struct { 71 | char name[NAMELENGTH]; 72 | char label[LABELLENGTH]; 73 | uint8_t type; 74 | int min; 75 | int max; 76 | uint8_t optionCnt; 77 | String options[MAXOPTIONS]; 78 | String labels[MAXOPTIONS]; 79 | } DESCRIPTION; 80 | 81 | class WebConfig { 82 | public: 83 | WebConfig(); 84 | //load form descriptions 85 | void setDescription(String parameter); 86 | //Add extra descriptions 87 | void addDescription(String parameter); 88 | //function to respond a HTTP request for the form use the filename 89 | //to save. 90 | #if defined(ESP32) 91 | void handleFormRequest(WebServer * server, const char * filename); 92 | //function to respond a HTTP request for the form use the default file 93 | //to save and restart ESP after saving the new config 94 | void handleFormRequest(WebServer * server); 95 | //get the index for a value by parameter name 96 | #else 97 | void handleFormRequest(ESP8266WebServer * server, const char * filename); 98 | //function to respond a HTTP request for the form use the default file 99 | //to save and restart ESP after saving the new config 100 | void handleFormRequest(ESP8266WebServer * server); 101 | //get the index for a value by parameter name 102 | #endif 103 | int16_t getIndex(const char * name); 104 | //read configuration from file 105 | boolean readConfig(const char * filename); 106 | //read configuration from default file 107 | boolean readConfig(); 108 | //write configuration to file 109 | boolean writeConfig(const char * filename); 110 | //write configuration to default file 111 | boolean writeConfig(); 112 | //delete configuration file 113 | boolean deleteConfig(const char * filename); 114 | //delete default configutation file 115 | boolean deleteConfig(); 116 | //get a parameter value by its name 117 | const String getString(const char * name); 118 | const char * getValue(const char * name); 119 | int getInt(const char * name); 120 | float getFloat(const char * name); 121 | boolean getBool(const char * name); 122 | //get the accesspoint name 123 | const char * getApName(); 124 | //get the number of parameters 125 | uint8_t getCount(); 126 | //get the name of a parameter 127 | String getName(uint8_t index); 128 | //Get results as a JSON string 129 | String getResults(); 130 | //Ser values from a JSON string 131 | void setValues(String json); 132 | //set the value for a parameter 133 | void setValue(const char*name,String value); 134 | //set the label for a parameter 135 | void setLabel(const char * name, const char* label); 136 | //remove all options 137 | void clearOptions(uint8_t index); 138 | void clearOptions(const char * name); 139 | //add a new option 140 | void addOption(uint8_t index, String option); 141 | void addOption(uint8_t index, String option, String label); 142 | //modify an option 143 | void setOption(uint8_t index, uint8_t option_index, String option, String label); 144 | void setOption(char * name, uint8_t option_index, String option, String label); 145 | //get the options count 146 | uint8_t getOptionCount(uint8_t index); 147 | uint8_t getOptionCount(char * name); 148 | //set form type to doen cancel 149 | void setButtons(uint8_t buttons); 150 | //register onSave callback 151 | void registerOnSave(void (*callback)(String results)); 152 | //register onSave callback 153 | void registerOnDone(void (*callback)(String results)); 154 | //register onSave callback 155 | void registerOnCancel(void (*callback)()); 156 | //register onSave callback 157 | void registerOnDelete(void (*callback)(String name)); 158 | 159 | //values for the parameter 160 | String values[MAXVALUES]; 161 | private: 162 | char _buf[2000]; 163 | uint8_t _count; 164 | String _apName; 165 | uint8_t _buttons = BTN_CONFIG; 166 | DESCRIPTION _description[MAXVALUES]; 167 | void (*_onSave)(String results) = NULL; 168 | void (*_onDone)(String results) = NULL; 169 | void (*_onCancel)() = NULL; 170 | void (*_onDelete)(String name) = NULL; 171 | }; 172 | 173 | #endif 174 | -------------------------------------------------------------------------------- /docs/Solderless.md: -------------------------------------------------------------------------------- 1 | # Building the DSC Circuit 2 | 3 | There are many ways to build the electronic circuit for this telescope DSC. 4 | 5 | This guide will focus on a very simple and solderless way to assemble the components, so that even people who don't own a soldering iron (or don't know how to use one) can build the DSC for their telescope. 6 | 7 | It is highly recommended that before you start building this circuit, you first upload the software to the ESP32 microcontroller as detailed in the [Upload and Configuration guide](https://github.com/vlaate/DobsonianDSC/blob/master/docs/UploadConfigure.md). 8 | 9 | ## Parts List 10 | 11 | There are very few components involved in this dual encoder DSC: 12 | 13 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/components.jpg "Parts List") 14 | 15 | * Two optical encoders with NPN open-collector outputs (discussed in more detail further below). 16 | * One "30 pin" ESP32 development board ([aliexpress](https://www.aliexpress.com/item/32800930387.html), [amazon](https://www.amazon.com/dp/B079PVCF2G)). 17 | * One keyes screw shield v2 ([keyestudio.com](https://www.keyestudio.com/), [aliexpress](https://www.aliexpress.com/item/2030646961.html)). 18 | * A set of 10 Dupont Cables 10cm long, either male-to-female or female-to-female ([aliexpress](https://www.aliexpress.com/item/32798042976.html)). 19 | 20 | Before you go on assembling the circuit, make sure you have already uploaded the software to the ESP-32. 21 | 22 | 23 | ## The Circuit 24 | 25 | The circuit for this DSC is very simple. The logical view tells you it's basically about connecting the 4 wires from each encoder to the ESP32 microcontroller: 26 | 27 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/Circuit_ESP32S.png "Logic Circuit") 28 | 29 | To make those connections easily and without needing to use a soldering iron, we'll use the *"keyes screw shield v2"* which is a small module that offers screw terminals and adapts them to dupont connectors: 30 | 31 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/keyes_screw_shield.jpg "keyes screw shield") 32 | 33 | The following diagram explains how the wiring should go from the ESP32 to this "screw terminal shield", and also shows which "green screw" terminals corresponds to which "black dupont" connectors: 34 | 35 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/solderless.jpg "Solderless Circuit") 36 | 37 | * Note that there 4 screw terminals labeled "**GND**", you can use any of them to connect the black GND wires from the optical encoders. 38 | * Note that there are 2 screw terminals labeled "**5V**", you can use any of them to connect the red 5V wires from the optical encoders. 39 | * The **A (Yellow)** and a **B (Green)** wires from the Azimuth encoder should go into the green screw terminals labeled **A0**, **A1**. 40 | * The **A (Yellow)** and a **B (Green)** wires from the Altitude encoder should go into the green screw terminals labeled **A2**, **A3**. 41 | 42 | After plugging the encoder wires into the appropriate screw terminals (green), you can use dupont cables to connect the dupont terminals (black) to the corresponding pins of the ESP-32, like this: 43 | 44 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/connected_2.jpg "Solderless") 45 | 46 | Make sure you are using the correct pins of the ESP-32: 47 | * The red 5V dupont connection should go to the ESP32 pin labeled **VIN**. 48 | * The black GND dupont connection should go to the ESP32 pin labeled **GND**. 49 | * The A and B connections from the A0 and A1 screw terminals should go to the ESP32 pins labeled **D18** and **D19**. 50 | * The A and B connections from the A2 and A3 screw terminals should go to the ESP32 pins labeled **D25** and **D26**. 51 | 52 | And that's it! If the connections are correct and the ESP32 was already programmed with the software, then the circuit is complete. 53 | 54 | 55 | ## About Encoders 56 | 57 | The resolution of your DSC will depend upon the resolution of the optical encoders you choose, and the gear ratio with which you affix those encoders to your telescope's axis. 58 | 59 | For encoders attached *directly* to the telescope axis (without any gear reduction), Encoders with resolutions of **2000 p/r** to **2500 p/r** are recommended. This will provide between 8.000 to 10.000 steps in a full circle, or between 2.2 and 2.7 arcminutes per step. 60 | 61 | For encoders connected to the telescope via some sort of gear reduction (such as GT2 gears and belt) the cheaper **600 p/r** encoders are sufficient, and for example attached through GT2 gears at a 4,25 gear ratio (85 teeth big gear to 20 teeth small gear) they will provide (600 * 4 * 4,25 = 10.200) 10.200 steps per full circle, or 2.1 arcminutes per step. 62 | 63 | Example with GT2 pulleys: 64 | 65 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/full_close.jpg "Close up AZ") 66 | 67 | In order to make this circuit as simple as possible, the optical encoders to be used **must** have **"NPN open-collector outputs"**. Encoders with other type of outputs (for example: *"Line Driver output"*) are not suited for this simple circuit and will require additional hardware such as level shifters. 68 | 69 | The most common encoder useable for this DSC project is the *Signswise / BQLZR* 600 p/r and it's generic clones. It's recommended to look for the mention of *"NPN open-collector"* in the vendor's page when dealing with generic ones. ([aliexpress](https://www.aliexpress.com/item/32669741048.html)). 70 | 71 | There seems to be a [source](https://www.aliexpress.com/item/32961497880.html) for higher resolution OMRON E6B2-CWZ6C encoders (of up to 2500 p/r) but I've not personally tested them. Note that only the CWZ6C family is NPN open-collector, other references (CWZ5B, CWZ3E, CWZ1X) are not. ([datasheet](http://www.ia.omron.com/data_pdf/cat/e6b2-c_ds_e_6_1_csm491.pdf)). 72 | 73 | If you are going to use GT2 gears, remember that the shaft width is 6mm for the Signswise, BQLZR and OMRON E6B2. 74 | 75 | A 3D printable bracket for the Signswise encoders can be found [here](https://www.cloudynights.com/topic/589521-37-dobsonian-dsc-for-diy-makers/page-5#entry8724826) 76 | 77 | If you know of other suitable encoders, you can let us know at the [CloudyNights forum](https://www.cloudynights.com/topic/589521-37-dobsonian-dsc-for-diy-makers/) 78 | 79 | 80 | ## About Different versions of the ESP-32 81 | 82 | The ESP32 microcontroller comes in different versions. This guide features the one with 30 pins / GPIOs, but there are versions with 38 pins, versions in the UNO form factor and in the Wemos Mini form factor, among others. Any version of the ESP32 can be used, but the pinouts illustrated in this guide are for the 30 pin version, so a beginer might be better off using exactly the 30 pin development board. 83 | 84 | 85 | ## About Battery Power 86 | 87 | The ESP32 boards typically come with a micro-USB connector, so USB power banks are the easiest solution to power the DSC. 88 | 89 | In order to use power sources with a DC barrel plug, such as the popular Talentcell 12V rechargeable batteries, or a 9V rechargeable with a plug adapter, there's a small module that can be used: 90 | 91 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/battery_options.jpg "Battery") 92 | 93 | That little [blue module](https://www.aliexpress.com/item/32806774850.html) can take any voltage from 7V to 12V and output 5V (and more than enough mA for the ESP32 and two encoders). so you can use it to power the DSC by connecting the "VIN" pin from the ESP32 and the red "5V" cable from the screw shield to the "5V OUT" pins of the blue module, and also the black GND cable from the screw shield plus the GND pin of the ESP32 to the GND pin of the blue module. 94 | 95 | It's not actually efficient because to drop the voltage to 5V, some energy must be wasted into heat, but it can be convenient, specially if you don't already own an USB power bank. 96 | -------------------------------------------------------------------------------- /docs/Solderless.es.md: -------------------------------------------------------------------------------- 1 | # Construyendo el Circuito del Apuntador Digital 2 | [[English]](https://github.com/vlaate/DobsonianDSC/blob/master/docs/Solderless.md) 3 | [[Español]](https://github.com/vlaate/DobsonianDSC/blob/master/docs/Solderless.es.md) 4 | 5 | Hay muchas formas de construir el circuito electrónico para éste proyecto de Apuntador Digital para Telescopio. 6 | 7 | La presente guía plantea una forma muy sencilla de ensamblar los componentes sin tener que soldar, de manera que incluso personas que no tengan un cautín (o no sepan cómo usarlo) puedan construir el ADT para su telescopio. 8 | 9 | Es necesario que antes de comenzar a ensamblar éste circuito, usted ya haya cargado el código del proyecto al microcontrolador ESP32 tal como se detalla en la [Guía de Carga y Configuración](https://github.com/vlaate/DobsonianDSC/blob/master/docs/UploadConfigure.es.md). 10 | 11 | ## Lista de Partes 12 | 13 | Éste proyecto de apuntador digital requiere muy pocos componentes: 14 | 15 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/components.jpg "Parts List") 16 | 17 | * Dos encoders ópticos tipo NPN open-collector (explicados en detalle más adelante). 18 | * Un microcontrolador ESP32 de "30 pines" ([aliexpress](https://www.aliexpress.com/item/32800930387.html), [amazon](https://www.amazon.com/dp/B079PVCF2G)). 19 | * Un módulo “keyes screw shield v2” ([keyestudio.com](https://www.keyestudio.com/products/keyes-screw-shield-v2-stud-terminal-expansion-board-double-support), [aliexpress](https://www.aliexpress.com/item/2030646961.html)). 20 | * Un juego de 10 cables Dupont de 10cm de largo, bien sea macho-hembra ó hembra-hembra ([aliexpress](https://www.aliexpress.com/item/32798042976.html)). 21 | 22 | Antes de comenzar a ensamblar el circuito, asegúrese de haber cargado primero el código del proyecto al dispositivo ESP-32. 23 | 24 | ## El Circuito 25 | 26 | El circuito de éste apuntador digital es muy sencillo. El diagrama lógico muestra que básicamente se trata de conectar los 4 cables de cada Encoder al microcontrolador ESP32: 27 | 28 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/Circuit_ESP32S.png "Logic Circuit") 29 | 30 | Para lograr éstas conexiones de manera fácil y sin tener que usar un cautín ni soldadura, usaremos el módulo *"keyes screw shield v2"* que es un pequeño módulo que ofrece terminales atornillables y los adapta a conectores tipo dupont: 31 | 32 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/keyes_screw_shield.jpg "keyes screw shield") 33 | 34 | El siguiente diagrama explica cómo sería el cableado desde el ESP32 hacia éste módulo "keyes screw shield", y también muestra cuales de los terminales atornillables verdes corresponden (vienen preconectados con) cuales de los terminales dupont negros: 35 | 36 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/solderless.jpg "Solderless Circuit") 37 | 38 | * Observe que hay 4 terminales atornillables verdes marcados como "**GND**", usted puede usar cualquiera de éstos para conectar los cables negros GND de los Encoders ópticos. 39 | * Observe que hay 2 terminales atornillables verdes marcados "**5V**", usted puede usar cualquiera de éstos para conectar los cables rojos 5V de los Encoders ópticos. 40 | * Los cables **A (Amarillo)** y **B (Verde)** del Encoder azimutal deben conectarse en los terminales verdes marcados como **A0** y **A1**. 41 | * Los cables **A (Amarillo)** y **B (Verde)** del Encoder altitudinal deben conectarse a los terminales verdes marcados como **A2** y **A3**. 42 | 43 | Después de conectar los cables de ambos Encoders a los terminales verdes atornillables correctos, use los cables dupont para conectar los terminales negros dupont con los pines correspondientes del ESP-32. Las conexiones se ven así: 44 | 45 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/connected_2.jpg "Solderless") 46 | 47 | Asegúrese de usar los pines correctos del ESP-32: 48 | * El cable rojo 5V debe conectarse al pin del ESP32 etiquetado como **VIN**. 49 | * El cable negro GND debe conectarse al pin del ESP32 etiquetado como **GND**. 50 | * Las conexiones A y B que vienen de los terminales A0 y A1 deben conectarse a los pines del ESP32 etiquetados como **D18** y **D19**. 51 | * Las conexiones A y B que vienen de los terminales A2 y A3 deben conectarse a los pines del ESP32 etiquetados como **D25** y **D26**. 52 | 53 | Y eso es todo! Si las conexiones están correctas y el ESP32 ya está programado con el software, entonces el circuito está completo. 54 | 55 | 56 | ## Acerca de los Encoders Ópticos 57 | 58 | La resolución del Apuntador Digital para Telescopio depende de la resolución de los encoders ópticos que utilice, y de la proporción de transmisión con la cual usted conecte los encoders a los ejes del telescopio. 59 | 60 | Para encoders conectados *directamente* al telescopio (sin usar engranajes ni reducción), se recomienda usar encoders con resoluciones de **2000 p/r** a **2500 p/r**. Ésto permitiría resoluciones de entre 8.000 a 10.000 pasos en 360 grados, es decir entre 2.2 a 2.7 minutos de arco por paso del encoder. 61 | 62 | Para encoders conectados al telescopio mediante algún tipo de transmisión o reducción (por ejemplo poleas tipo GT2) los encoders económicos de **600 p/r** son suficientes, y por ejemplo usando poleas GT2 a una relación de transmisión de 4,25 (una polea grande de 85 dientes conectada a una polea pequeña de 20 dientes) se lograría una resolución de (600 * 4 * 4,25 = 10.200) 10.200 pasos por 360 grados, es decir de 2.1 minutos de arco por paso. 63 | 64 | Ejemplo con poleas GT2: 65 | 66 | ![alt text](https://github.com/vlaate/DobsonianDSC/blob/master/img/full_close.jpg "Close up AZ") 67 | 68 | Para lograr que el circuito sea lo más sencillo posible, los encoders ópticos a usar **deben** tener salidas tipo **"NPN open-collector"**. Encoders con otro tipo de salidas (por ejemplo: *"Line Driver output"*) no sirven con éste sencillo circuito, pues requerirían hardware adicional. 69 | 70 | El tipo de Encoder más común utilizable en éste proyecto de apuntador digital es el *Signswise / BQLZR* 600 p/r y sus clones genéricos. Se recomienda buscar que la página del vendedor mencione *"NPN open-collector"* cuando compre los genéricos. ([aliexpress](https://www.aliexpress.com/item/32669741048.html)). 71 | 72 | Parece existir un [vendedor](https://www.aliexpress.com/item/32961497880.html) de Encoders de alta resolución OMRON E6B2-CWZ6C (que dan hasta 2500 p/r) pero no los he probado personalmente. Recuerde que únicamente la familia CWZ6C es “NPN open-collector”, otras familias de la marca OMRON (ej: CWZ5B, CWZ3E, CWZ1X) no servirían. ([datasheet](http://www.ia.omron.com/data_pdf/cat/e6b2-c_ds_e_6_1_csm491.pdf)). 73 | 74 | Si usted va a usar poleas GT2 (las de las impresoras 3D) como transmisión, para las poleas pequeña recuerde que el eje de los Encoders tiene un ancho de 6mm, tanto para los Signswise, BQLZR como para los OMRON E6B2. 75 | 76 | [Aquí](https://www.cloudynights.com/topic/589521-37-dobsonian-dsc-for-diy-makers/page-5#entry8724826) puede encontrar un diseño de sujetador para los encoders Signswise para crearlo con una impresora 3D. También puede hacer uno sencillo con madera de balso. 77 | 78 | Si conoce otros encoders tipo NPN open-collector que puedan funcionar con el proyecto, puede hacernos saber en los foros de [CloudyNights forum](https://www.cloudynights.com/topic/589521-37-dobsonian-dsc-for-diy-makers/) 79 | 80 | ## Acerca de diferentes versiones del ESP-32 81 | 82 | El microcontrolador ESP32 viene en diferentes versiones. La presente guía se basa en la versión de 30 pines / GPIOs, pero hay versiones con 38 pines, y versiones en otros formatos como el tipo UNO o el tipo Wemos Mini. Cualquier versión del ESP32 puede usarse para construir el proyecto, pero los diagramas y pines ilustrados en ésta guía son para la versión de 30 pines, por lo que para un principiante sería mejor usar esa placa de desarrollo ESP32 específicamente. 83 | 84 | ## Fuente de Energía 85 | 86 | Las placas ESP32 generalmente vienen con un conector micro-USB, de manera que un banco de poder USB (de los usados para recargar teléfonos celulares) sería la solución mas sencilla para darle energía al apuntador digital. 87 | 88 | Si desea usar otras fuentes de energía de los que tienen un conector DC tipo “plug”, como las populares baterías recargables Talentcell de 12V, or una pila de 9V recargable con adaptador plug, hay un pequeño módulo que se puede usar: 89 | 90 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/battery_options.jpg "Battery") 91 | 92 | Ese pequeño [módulo azul](https://www.aliexpress.com/item/32806774850.html) puede recibir voltajes entre 7V a 12V y entregar 5V regulados con suficiente corriente para energizar el proyecto. Para usarlo, conecte los pines "5V OUT" del módulo con el pin "VIN" del ESP32 y con el cable rojo "5V" del terminal atornillable. También conecte los pines “GND” del módulo azul con el pin “GND” del ESP32 y con el cable negro GND del terminal atornillable. 93 | 94 | No es una forma eficiente de darle energía al proyecto ya que para bajar el voltaje a 5V, se desperdicia algo de energía en forma de calor, pero puede ser una solución conveniente para quienes no tienen un banco USB, pero si tienen una batería portátil de 12V. 95 | 96 | -------------------------------------------------------------------------------- /docs/ArduinoIDE.md: -------------------------------------------------------------------------------- 1 | # Arduino IDE Beginners Guide 2 | [[English]](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.md) 3 | [[Español]](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.es.md) 4 | 5 | The purpose of this guide is to help someone who wants to build the [Telescope DSC project](https://github.com/vlaate/DobsonianDSC), but who has never had any experience with Arduino, microcontrollers, or anything similar. As such, this guide is for the absolute beginner, and 6 | it will provide step by step instructions on how to install and configure the Arduino IDE on your computer, connect your microcontroller, and validate that things are working as intended. 7 | 8 | ### 1. Download the Arduino IDE Software 9 | 10 | Go to https://www.arduino.cc/en/software and download the Arduino IDE for your operating system. As of 2021 I recommend using the Version 1.8.15 or later. 11 | 12 | ### 2. Install 13 | 14 | Launch the downloaded installer, make sure all the components are selected (they are by default, even the "USB driver") and let it complete installation: 15 | 16 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/2.png "Installing IDE") 17 | 18 | ### 3. Launch IDE 19 | 20 | Now that you have installed the Arduino IDE (Integrated Development Environment) you'll see a new Arduino icon on your computer. Use it to Launch the Arduino Integrated Development Environment. 21 | 22 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/3.png "Launch IDE") 23 | 24 | This is the software used to compile the arduino programs and upload them (via USB) to your ESP32 microcontroller. 25 | 26 | ### 4. Add Board Manager URLs 27 | 28 | For the Arduino IDE to be able to work with ESP32 and similar microcontrollers, it needs to download their "board manager". 29 | 30 | To do this, in the top menu of the Arduino IDE select "File" -> "Preferences": 31 | 32 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/4.png "Add URLs") 33 | 34 | In the emerging window there's a setting called "Additional Boards Manager URLs". 35 | 36 | Put the following value in it: 37 | 38 | ``` 39 | https://dl.espressif.com/dl/package_esp32_index.json, http://arduino.esp8266.com/stable/package_esp8266com_index.json 40 | ``` 41 | 42 | ### 5. Install Board Managers 43 | 44 | Click "OK" to close the preferences window, and back at the arduino top menu, select "Tools" -> "Board: XXX" -> "Boards Manager" 45 | 46 | The boards manager window pops up. The text field is a search field. Type "ESP32" on it, and wait for the list of boards to update. When a board called "esp32 by Espressif Systems" shows up, click on it's "Install" button: 47 | 48 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/5.png "Install ESP32 BM") 49 | 50 | Once it's done installing, click the "Close" button. 51 | 52 | ### 6. Install Libraries 53 | 54 | The Telescope DSC project depends on a couple of open source libraries. To install them, do the following: 55 | 56 | In the top menu, select "Tools" -> "Manage Libraries" 57 | 58 | The library manager window pops up. The text field is a search field. Type "ESP32Encoder" on it, and wait for the list of libraries to update. When a library called *"ESP32Encoder by Kevin Harrington"* shows up, click on it's "Install" button. 59 | 60 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/6.png "Install ESP32Encoder") 61 | 62 | After the encoder library is installed, change the search field to "ArduinoJson" and look for a library called *"ArduinoJson by Benoit Blanchon"* and Install it too. When finished, close the library manager window. 63 | 64 | ### 7. Select Board 65 | 66 | Now that your Arduino IDE is ready to compile programs for the ESP32 family of microcontrollers, it's time to configure it for your particular microcontroller. 67 | 68 | In the top arduino menu, select "Tools" -> "Board: XXX" -> "ESP32 Arduino" 69 | 70 | You will see a long list of ESP32 boards supported by the IDE. If you are going to use the ESP-32S Development Board that is recommended for the Telescope DSC project, then choose "ESP32 Dev Module". 71 | 72 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/7.png "Select Board") 73 | 74 | ### 8. Configure Memory Partition Scheme 75 | 76 | The telescope DSC project takes a lot of memory (mostly because bluetooth libraries are big). 77 | In order to prevent a compilation error saying *"Sketch too big"*, you need to assign more of the microcontroller's memory to the app (and less to the filesystem). 78 | 79 | So, in the top arduino IDE menu select "Tools" -> "Partition Scheme: XX" -> *"Huge App 3MB no OTA/1MB SPIFFS"*. This will prevent you from getting the error detailed above (orange text): 80 | 81 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/8.png "Sketch Too Big") 82 | 83 | **Note:** Part of the reason why I recommended you to select the "ESP32 Dev Module" in a previous step was that a few other boards don't allow you to change partition scheme (for example the NodeMCU32-S does not allow it). If you decide to use a different board such as WROOM or a Wemos D1 ESP32, etc, look for Partition Scheme settings called "Huge App" or similar. 84 | 85 | ### 9. Connect the Microcontroller 86 | 87 | Now it's time to tell your IDE how to communicate with your microcontroller device. Plug your USB cable to your ESP32 microcontroller, and plug the other end to your computer. 88 | 89 | Under normal circumstances, the needed drivers should already be bundled with your operating system and automatically installed upon connecting the ESP board to the PC. If you are using an old OS, or run into problems, check the guide from Espressiff (the ESP manufacturer) which contains links for drivers: 90 | 91 | https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/establish-serial-connection.html 92 | 93 | ### 10. Identify COM Port 94 | 95 | Once the USB driver for the microcontroller ins properly installed, it's time to check that you can 96 | connect to it properly. 97 | 98 | In the Arduino IDE top menu, select "Tools" -> "Port: XX" 99 | 100 | You will see a list of serial ports (COM1, COM5, etc). You are supposed to choose the one that was assigned to your ESP32 microcontroller: 101 | 102 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/10.png "Com Ports") 103 | 104 | **¿How do I know which COM port is the one assigned to my USB-connected microcontroller?** 105 | 106 | A simple way is: While the USB cable is still connected, take note of all the names on the ports list. Then click with your mouse somewhere else on the IDE so that the menu (and the list of com ports) is no longer visible. Then Unplug the USB cable. Then select "Tools" -> "Port: XX" again to view the list. Whichever name dissappeared from the list after unplugging the USB cable, that's the COM port that corresponds to your ESP32 microcontroller. 107 | 108 | ### 11. Validate USB Connection 109 | 110 | Make sure your USB cable is plugged to both your ESP32 microcontroller and your computer. 111 | 112 | Then in the Arduino IDE top menu, select "Tools" -> "Get Board Info" 113 | 114 | You should see a small pop-up window that contains either the serial number of your microcontroller board, or the message *"SN: upload any sketch to obtain it"*: 115 | 116 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/11.png "Board Info") 117 | 118 | If you get a similar message to this, all is OK. If you get a small message on the green bar saying "can't obtain info", try with a different COM port. 119 | 120 | ### 12. Creating a Test Sketch 121 | 122 | Now we will use a very simple program ("sketch" in Arduino parlance) to verify that things are working properly. 123 | 124 | In the Arduino IDE, select "File" - > "New" 125 | 126 | In the new IDE window, paste the following sketch: 127 | 128 | ``` C 129 | /** Simple Echo sketch to validate serial communication */ 130 | # define BUFFER_SIZE 80 131 | char buf[BUFFER_SIZE]; 132 | 133 | int readLineFromSerial(char *buffer) 134 | { 135 | static int pos = 0; 136 | int rpos; 137 | int character = Serial.read(); 138 | 139 | if (character > 0) { 140 | switch (character) { 141 | case '\r': 142 | break; 143 | case '\n': 144 | rpos = pos; 145 | pos = 0; 146 | return rpos; 147 | default: 148 | if (pos < BUFFER_SIZE - 1) { 149 | buffer[pos++] = character; 150 | buffer[pos] = 0; 151 | } 152 | } 153 | } 154 | return 0; 155 | } 156 | 157 | void setup() { 158 | Serial.begin(115200); 159 | Serial.println("Waiting for user message"); 160 | } 161 | 162 | void loop() 163 | { 164 | if (readLineFromSerial(buf) > 0) 165 | { 166 | Serial.print("Recieved: "); 167 | Serial.println(buf); 168 | Serial.println("\nWaiting for user message"); 169 | } 170 | } 171 | ``` 172 | 173 | 174 | Now select "File" -> "Save" and proceed to save the sketch somewhere in your computer. 175 | 176 | ### 13. Uploading the Sketch 177 | 178 | Next step is to "upload" the sketch to the ESP32 microcontroller via the USB vable. To do this, you can either select "Sketch" -> "Upload" or click on the round icon with an arrow pointing right: 179 | 180 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/12.png "Upload") 181 | 182 | When you do this, the bottom part of the IDE (black background with orange text) will show a lot of messages from the compiling and uploading process. For this screenshot I enlarged the messages section, but it's normally just 3 lines tall: 183 | 184 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/13.png "Uploaded") 185 | 186 | If instead of the above messages you see the message: *"Connecting.....------...."* followed by **"Failed to connect to ESP32: Timed out waiting for packet header"**, this is solved by trying again (click "Sketch" -> "Upload") and as soon as you see the *"Connecting..."* message, hold down the physical button labeled "BOOT/FLASH" on the ESP32 board for a few seconds. 187 | 188 | The indicator that the upload has completed successfully, is that the little green bar will contain the message "Done uploading." as seen on the above screenshot. 189 | 190 | If you get this message then all is well: the program has been compiled and uploaded to your ESP microcontroller. In fact, it's proably already being executed in the device. 191 | 192 | 193 | ### 14. Validating the ESP32 microcontroller works 194 | 195 | What the sketch you uploaded does, is recieve messages and respond with an echo. This will enable us to validate that the hardware is properly working, including the ESP32 microcontroller itself, and the USB connection to it. 196 | 197 | To test this, select "Tools" -> Serial Monitor". 198 | 199 | A small window shows up. At the bottom-right of the window, there is a combo-box. Use it to select "115200" which is the communication speed out test sketch expects to use: 200 | 201 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/14.png "Baud Rate") 202 | 203 | Now in the top of the serial monitor window, there's a text box. Type something into it (for example "Hello") and click the "Send" button. 204 | 205 | You should see a reply from the microcontroller containing whatever text you sent, like this: 206 | 207 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/15.png "Success") 208 | 209 | You can type and send more messages, and the ESP32 should reply. This is evidence that the ESP32 board and the USB connection are working properly. 210 | 211 | And that's it! If you got this far and things worked as described in the steps, then you've successfully set up your computer to program an ESP32 microcontroller, and you're ready for the [next guide](https://github.com/vlaate/DobsonianDSC/blob/master/docs/UploadConfigure.md). 212 | -------------------------------------------------------------------------------- /docs/ArduinoIDE.es.md: -------------------------------------------------------------------------------- 1 | # Guía de Arduino para Principiantes 2 | [[English]](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.md) 3 | [[Español]](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.es.md) 4 | 5 | 6 | El propósito de esta guía es ayudar a astrónomos aficionados que quieran construir el [proyecto de Apuntador Digital para Telescopio](https://github.com/vlaate/DobsonianDSC), pero que nunca hayan tenido experiencia previa con Arduino ni microcontroladores. 7 | 8 | Como tal, ésta guía es para novatos y ofrece instrucciones paso a paso desde cómo instalar y configurar el IDE de Arduino en su computadora, hasta cómo conectar su microcontrolador, cargarle un programa y verificar que funcione correctamente. 9 | 10 | ### 1. Descargar el Entorno de Desarrollo Arduino 11 | 12 | Visite https://www.arduino.cc/en/software y descargue el software de desarrollo (IDE) Arduino para su sistema operativo. A la fecha (2021) recomiendo usar la versión 1.8.15 or superior. 13 | 14 | ### 2. Instalar Arduino 15 | 16 | Ejecute el instalador que ha descargado, asegúrese de que todos los componentes de Arduino estén seleccionados para ser instalados (incluso el "USB driver") y espere a que se complete la instalación: 17 | 18 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/2.png "Installing IDE") 19 | 20 | ### 3. Abrir el IDE Arduino 21 | 22 | Ahora que ya tiene instalado el Entorno de Desarrollo Arduino (Arduino IDE) podrá encontrar un nuevo ícono de Arduino en su computador. Úselo para ejecutar el IDE Arduino. 23 | 24 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/3.png "Launch IDE") 25 | 26 | Éste es el software que utilizará para compilar programas tipo Arduino y cargarlos (vía USB) a su microcontrolador ESP32. 27 | 28 | ### 4. Agregar URLs de Placas 29 | 30 | Para que el IDE Arduino pueda funcionar con microcontroladores ESP32 y similares, necesita primero descargar su “manejador de placas” (board manager). 31 | 32 | Para hacer esto, en el menú principal del IDE Arduino seleccione "File" -> "Preferences": 33 | 34 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/4.png "Add URLs") 35 | 36 | En la ventana emergente hay una configuración llamada “Additional Boards Manager URLs". 37 | 38 | Copie y pegue el siguiente valor en esa opción: 39 | 40 | 41 | ``` 42 | https://dl.espressif.com/dl/package_esp32_index.json, http://arduino.esp8266.com/stable/package_esp8266com_index.json 43 | ``` 44 | 45 | ### 5. Instalar Manejadores de Placas 46 | 47 | Haga click en "OK" para cerrar la ventana de preferencias, y de regreso en el menú principal de Arduino, seleccione "Tools" -> "Board: XXX" -> "Boards Manager" 48 | 49 | Aparecerá la ventana emergente del manejador de placas. El campo de texto es un campo de búsqueda, escriba "ESP32" en ese campo y espere a que la lista de placas se actualice. Cuando aparezca una placa llamada *"esp32 by Espressif Systems"*, haga click en el botón "Install" a su derecha: 50 | 51 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/5.png "Install ESP32 BM") 52 | 53 | Una vez termina de instalar, haga click en el botón "Close". 54 | 55 | ### 6. Instalar Librerías 56 | 57 | El proyecto de Apuntador Digital para Telescopio (ADT) necesita un par de librerías de código abierto. Para instalarlas, haga lo siguiente: 58 | 59 | En el menú principal, seleccione "Tools" -> "Manage Libraries" 60 | 61 | Aparecerá la ventana del manejador de librerías. El campo de texto es para hacer búsquedas, escriba "ESP32Encoder" allí y espere a que la lista de librerías se actualice. Cuando aparezca una librería llamada *"ESP32Encoder by Kevin Harrington"* haga click en su botón "Install". 62 | 63 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/6.png "Install ESP32Encoder") 64 | 65 | Una vez la librería ESP32Encoder esté instalada, cambie el texto de búsqueda por "ArduinoJson" y espere a que aparezca una librería llamada *"ArduinoJson by Benoit Blanchon"* y haga click en su botón “Install” para instalarla de la misma manera que la anterior. Una vez haya finalizado instalando ambas librerías, cierre la ventana del manejador de librerías. 66 | 67 | ### 7. Seleccionar Placa 68 | 69 | Ahora que su IDE Arduino está lista para compilar programas para la familia de microcontroladores ESP32, es hora de configurarla para el microcontrolador específico que vamos a usar para el proyecto. 70 | 71 | En el menú principal del IDE, seleccione "Tools" -> "Board: XXX" -> "ESP32 Arduino" 72 | 73 | Aparecerá una lista larga de placas ESP32 boards soportadas por el entorno de desarrollo. Se recomienda elegir "ESP32 Dev Module". 74 | 75 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/7.png "Select Board") 76 | 77 | ### 8. Configurar el Particionamiento de Memoria 78 | 79 | El proyecto de Apuntador Digital para Telescopio ocupa bastante memoria (principalmente debido a que las librerías Bluetooth son muy grandes). 80 | Para evitar que le aparezca un error de compilación diciendo *"Sketch too big"*, es necesario asignar una porción mayor de la memoria del microcontrolador a la aplicación. 81 | 82 | Para ello, seleccione en el menú principal del IDE la opción "Tools" -> "Partition Scheme: XX" -> *"Huge App 3MB no OTA/1MB SPIFFS"*. Con ésto se evita que al compilar se aparezca el siguiente error (ver texto en naranja): 83 | 84 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/8.png "Sketch Too Big") 85 | 86 | **Nota:** Parte de la razón por la cual recomiendo seleccionar la placa "ESP32 Dev Module" en un paso anterior, es porque otras placas no permiten cambiar el particionamiento de memoria (por ejemplo la placa NodeMCU32-S no lo permite). Si usted decide usar una placa distinta como la Wemos D1 ESP32 o similar, busque opciones de particionamiento con un nombre parecido a "Huge App". 87 | 88 | ### 9. Conectar el Microcontrolador 89 | 90 | Ahora es necesario enseñarle al IDE Arduino cómo comunicarse con su microcontrolador. Conecte un extremo del cable USB a su to microcontrolador ESP32, y el otro extremo a su computador. 91 | 92 | En circunstancias normales, los drivers necesarios ya deberían venir con su sistema operativo e instalarse automáticamente con sólo conectar el ESP32 vía USB al computador. Si su computador tiene un sistema operativo antiguo, revise la guía de Espressiff (el fabricante del ESP32) la cual contiene enlaces a diferentes drivers: 93 | 94 | https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/establish-serial-connection.html 95 | 96 | ### 10. Identificar el Puerto Serial COM 97 | 98 | Una vez el driver USB para el microcontrolador esté instalado, es hora de verificar que el IDE se pueda conectar correctamente al dispositivo. 99 | 100 | En el menú principal del IDE, seleccione "Tools" -> "Port: XX" 101 | 102 | Verá que aparece una lista de puertos seriales (ejemplo: COM1, COM5, etc). Se supone que usted debe elegir el puerto serial COM que su computador le asignó al microcontrolador ESP32: 103 | 104 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/10.png "Com Ports") 105 | 106 | **¿Cómo averiguo cual de los puertos COM de la lista es el que está asignado al microcontrolador que está conectado por USB??** 107 | 108 | Una forma sencilla de averiguarlo es: Mientras el cable USB todavía está conectado, tome nota de todos los nombres en la lista de puertos COM. Entonces haga click con el ratón en algún otro lugar del IDE para que el menú (y la lista de puertos COM) se oculte. Entonces desconecte el cable USB, y vuelva a seleccionar "Tools" -> "Port: XX" en el menú, para volver a ver la lista de puertos COM. El nombre que haya desaparecido de la lista después de desconectar el cable USB, ese es el puerto serial COM que corresponde a su microcontrolador ESP32. 109 | 110 | ### 11. Valide la conexión USB 111 | 112 | Asegúrese de que el cable USB cable está conectado tanto al ESP32 como al computador. 113 | 114 | Entonces, en el menú principal del IDE Arduino, seleccione "Tools" -> "Get Board Info". 115 | 116 | Debería ver aparecer una pequeña ventana emergente que contiene el número serial del microcontrolador ó el mensaje *"SN: upload any sketch to obtain it"*: 117 | 118 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/11.png "Board Info") 119 | 120 | Si usted obtiene un mensaje similar a éste, significa que todo está bien. Si usted obtiene un pequeño mensaje en la barra verde del IDE Arduino diciendo "can't obtain info", intente con un puerto serial COM diferente. 121 | 122 | ### 12. Creando un programa de prueba 123 | 124 | Ahora vamos a usar un programa sencillo para verificar que el microcontrolador esté funcionando correctamente. 125 | 126 | En el IDE Arduino, seleccione "File" - > "New" 127 | 128 | En la nueva ventana del IDE que aparece, copie y pegue el siguiente contenido: 129 | 130 | ``` C 131 | /** Programa Eco sencillo para validar enlace serial */ 132 | # define BUFFER_SIZE 80 133 | char buf[BUFFER_SIZE]; 134 | 135 | int readLineFromSerial(char *buffer) 136 | { 137 | static int pos = 0; 138 | int rpos; 139 | int character = Serial.read(); 140 | 141 | if (character > 0) { 142 | switch (character) { 143 | case '\r': 144 | break; 145 | case '\n': 146 | rpos = pos; 147 | pos = 0; 148 | return rpos; 149 | default: 150 | if (pos < BUFFER_SIZE - 1) { 151 | buffer[pos++] = character; 152 | buffer[pos] = 0; 153 | } 154 | } 155 | } 156 | return 0; 157 | } 158 | 159 | void setup() { 160 | Serial.begin(115200); 161 | Serial.println("Esperando mensaje"); 162 | } 163 | 164 | void loop() 165 | { 166 | if (readLineFromSerial(buf) > 0) 167 | { 168 | Serial.print("Recibido: "); 169 | Serial.println(buf); 170 | Serial.println("\nEsperando Mensaje"); 171 | } 172 | } 173 | ``` 174 | 175 | 176 | Ahora seleccione en el menú la opción "File" -> "Save" y proceda a grabar el programa en algún lugar de su computador. 177 | 178 | ### 13. Cargando el Programa 179 | 180 | El siguiente paso es “cargar” el programa al microcontrolador ESP32 usando el cable USB. Para hacer ésto, usted puede seleccionar en el menú "Sketch" -> "Upload" ó hacer click en el ícono redondo que tiene una flecha apuntando a la derecha: 181 | 182 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/12.png "Upload") 183 | 184 | Al hacer ésto, la parte de abajo del IDE (fondo negro con texto naranja) empezará a mostrar una gran cantidad de mensajes mientras se compila y se carga el programa. Para ésta foto agrandé la sección de mensajes del IDE: 185 | 186 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/13.png "Uploaded") 187 | 188 | Si en lugar de los mensajes anteriores, el mensaje que le aparece dice: *"Connecting.....------...."* seguido de **"Failed to connect to ESP32: Timed out waiting for packet header"**, ésto se resuelve intentando de nuevo (haga click en "Sketch" -> "Upload") y tan pronto aparezca el mensaje que dice **"Connecting..."** presiona el botón físico del ESP32 que está marcado como "BOOT" y manténgalo presionado durante varios segundos. 189 | 190 | La señal de que la carga ha sido exitosa es que en la barra verde del IDE aparece el mensaje "Done uploading." como se ve en la foto de arriba. 191 | 192 | Si usted obtiene ese mensaje significa que todo funcionó y que el programa quedó compilado y cargado en el microcontrolador. De hecho, probablemente ya se está ejecutando en el pequeño dispositivo. 193 | 194 | ### 14. Validando que el microcontrolador ESP32 funcione 195 | 196 | Lo que hace el programa ejemplo que hemos cargado al ESP32 es recibir mensajes y responder con un “eco”. Ésto nos permitirá validar que el hardware esté funcionando correctamente, incluyendo la conexión serial vía USB. 197 | 198 | Para validarlo, seleccione en el menú "Tools" -> Serial Monitor". 199 | 200 | Aparecerá una pequeña ventana, que en su parte inferior derecha tiene un combo de selección. Úselo para seleccionar "115200" que es la velocidad de comunicación que el programa de prueba espera usar: 201 | 202 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/14.png "Baud Rate") 203 | 204 | Ahora, puede ver que en la parte superior de la ventana hay un campo de texto. Escriba algo allí (cualquier cosa, por ejemplo “Hola”) y haga click en el botón "Send". 205 | 206 | Debería ver una respuesta del microcontrolador, que contiene el mismo texto que usted le envió, por ejemplo: 207 | 208 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/15.png "Success") 209 | 210 | Usted puede escribir y enviar más mensajes, y el ESP32 debería responder. Esto es evidencia de que el programa quedó correctamente cargado en la placa ESP32 y de que la conexión serial vía USB funciona correctamente. 211 | 212 | Y eso es todo! Si llegó hasta éste punto y todo funcionó como se describe en ésta guía, entonces su computador ya está listo para programar microcontroladores ESP32 y usted está listo para la [siguiente guía](https://github.com/vlaate/DobsonianDSC/blob/master/docs/UploadConfigure.md). 213 | 214 | -------------------------------------------------------------------------------- /AccelDSC/AccelDSC.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Digital Setting Circles for Dobsonian Telescopes -Accelerometer version- 3 | For dual encoder version check https://github.com/vlaate/DobsonianDSC 4 | 5 | This is a simple adapter to connect an inexpensive incremental optical encoder 6 | and an accelerometer+compass (LSM303DLHC) via WiFi to the SkySafari Android/iOS app, 7 | in order to provide "Push To" style features to dobsonian style telescope mounts 8 | including tabletops. 9 | 10 | Copyright (c) 2017 Vladimir Atehortua. All rights reserved. 11 | This program is free software: you can redistribute it and/or modify 12 | it under the terms of the version 3 GNU General Public License as 13 | published by the Free Software Foundation. 14 | This program is distributed in the hope that it will be useful, 15 | but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | GNU General Public License for more details. 18 | You should have received a copy of the GNU General Public License along with this program. 19 | If not, see 20 | */ 21 | 22 | /** 23 | Hardware used: 24 | NodeMCU ESP8266 development board (version 12E) I used the HiLetgo model https://www.amazon.com/gp/product/B010O1G1ES 25 | LSM303DLHC sensor module, I used this one: https://www.aliexpress.com/item/1956617486.html 26 | ESP8266 pinout: 27 | SDA = GPIO2 = PIN_D4 (use 3.3K pullup to 3V3) 28 | SCL = GPIO0 = PIN_D3 (use 3.3K pullup to 3V3) 29 | Encoder_A = GPIO4 = PIN_D2 30 | Encoder_B = GPIO5 = PIN_D1 31 | VIN = Encoder VCC = 5V power source 32 | LSM303 pinout: 33 | SDA, SCL and GND: matching the ESP8266 34 | VCC = 3V3 pin on the ESP8266 35 | The sensor is meant to be placed sideways (with the Z axis horizontal, on the left side of the telescope OTA) 36 | */ 37 | 38 | #include // Install this on Arduino IDE: "Encoder Library by Paul Stoffregen" (I used version 1.4.1), https://www.pjrc.com/teensy/td_libs_Encoder.html 39 | #include // built in library from Arduino IDE 40 | #include // Install this on Arudino IDE: "LSM303 Library by Pololu" (I used version 3.0.1), https://github.com/pololu/lsm303-arduino 41 | #include // built in library from Arduino IDE 42 | #define RADIAN_TO_STEPS 1623.3804f // ratio to convert from radians to encoder steps, using a "full circle" of 10200 steps as the Skysafari setting for basic encoder 43 | #define STEPS_IN_FULL_CIRCLE 10200 // number of steps in a full circle, should match the skysafari setting for basic encoder 44 | #define EMA_WEIGHT 0.05f // weight coefficient for new values vs old values used in the exponential moving average smoothing algorithm 45 | 46 | // To give sufficient CPU time to the TCP Server, a time delta between measurements is enforced: 47 | #define MEASUREMENT_PERIOD 50 // how many milliseconds must have elapsed since last measurement in order to take a new one 48 | 49 | long last_measurement = 0; // millisecond timestamp of last measurement, to measure only every XXX milliseconds 50 | 51 | WiFiServer server(4030); // 4030 is the default port Skysafari uses for WiFi connection to telescopes 52 | WiFiClient remoteClient; // represents the connection to the remote app (Skysafari) 53 | #define WiFi_Access_Point_Name "TelescopeDSC" // Name of the WiFi access point this device will create for your tablet/phone to connect to. 54 | 55 | long oldPosition = -999; 56 | 57 | /** Extends the Pololu LSM303 driver class, to add calibration of accelerometer data, and provide both altitude and azimuth readings after smoothing and in optical encoder style. 58 | */ 59 | class IMU : public LSM303 60 | { 61 | // Calibration of accelerometer: These are the minimum and maximum values measured for X, Y and Z: 62 | LSM303::vector acc_min = (LSM303::vector) {-16649, -16448, -16654}; 63 | LSM303::vector acc_max = (LSM303::vector) {+16108, +16568, +15599}; 64 | 65 | // measurements are stored here after calling calculatePosition(); 66 | public : float azimuthReading; // multiply this by 180 / PI to get degrees 67 | public : float altitudeReading; // multiply this by 180 / PI to get degrees 68 | 69 | public : float smoothAzimuthReading; // value is already converted from radians to steps, and smoothing alorithm applied 70 | public : float smoothAltitudeReading; // value is already converted from radians to steps, and smoothing alorithm applied 71 | 72 | vector East = {0, 0, 0}; 73 | 74 | public : void calculatePosition() 75 | { 76 | vector zAxis = (vector) {0, 0, 1}; // the heading will be measured relative to Z axis, make sure to place your sensor in a vertical position (Z axis pointing to the horizon) 77 | 78 | // get raw accelerometer data: 79 | vector acc_reading = {a.x, a.y, a.z}; 80 | 81 | // calibrate accelerometer bias and scale using measured maximums and minimums, and apply smoothing algorithm to reduce outlier values: 82 | acc_reading.x = acc_reading.x * (1 - EMA_WEIGHT) + EMA_WEIGHT * (32767.0 * ((float)a.x - (float)acc_min.x) / ((float)acc_max.x - (float)acc_min.x) - 16383.5); 83 | acc_reading.y = acc_reading.y * (1 - EMA_WEIGHT) + EMA_WEIGHT * (32767.0 * ((float)a.y - (float)acc_min.y) / ((float)acc_max.y - (float)acc_min.y) - 16383.5); 84 | acc_reading.z = acc_reading.z * (1 - EMA_WEIGHT) + EMA_WEIGHT * (32767.0 * ((float)a.z - (float)acc_min.z) / ((float)acc_max.z - (float)acc_min.z) - 16383.5); 85 | 86 | // calculate the altitude as an angle from 0º to 90º, see equation #26 from https://cache.freescale.com/files/sensors/doc/app_note/AN3461.pdf 87 | altitudeReading = -1 * atan2(acc_reading.y, sqrt(acc_reading.x * acc_reading.x + acc_reading.z * acc_reading.z)); 88 | 89 | // now adapt the 0º to 90º reading to the 0º to 360º style encoder output expected by Skysafari: 90 | if (acc_reading.x > 0 && acc_reading.y < 0) 91 | { 92 | // first quadrant 93 | } 94 | else if (acc_reading.x < 0 && acc_reading.y < 0) 95 | { 96 | // second quadrant 97 | altitudeReading = PI - altitudeReading; 98 | } 99 | else if (acc_reading.x < 0 && acc_reading.y > 0) 100 | { 101 | // third quadrant 102 | altitudeReading = PI - altitudeReading; 103 | } 104 | else if (acc_reading.x > 0 && acc_reading.y > 0) 105 | { 106 | altitudeReading = 2 * PI + altitudeReading; 107 | // 4th quadrant 108 | } 109 | 110 | float newAltitudeReading = altitudeReading * RADIAN_TO_STEPS; 111 | 112 | /* Final smoothing algotithm: */ 113 | // When the new readings are less than 0.5 degrees off from the old readings, use 0.05 * EMA_WEIGHT as the alpha, for a highly smoothed result 114 | if (abs(newAltitudeReading - smoothAltitudeReading) < STEPS_IN_FULL_CIRCLE / 720) 115 | { 116 | smoothAltitudeReading = newAltitudeReading * EMA_WEIGHT * 0.05 + ((1 - EMA_WEIGHT * 0.05) * smoothAltitudeReading); 117 | } 118 | else // When the new readings are more than 0.5 degrees off from the old readings, the regular EMA_WEIGHT as the alpha, fast, responsive user experience 119 | { 120 | smoothAltitudeReading = newAltitudeReading * EMA_WEIGHT + ((1 - EMA_WEIGHT) * smoothAltitudeReading); 121 | } 122 | 123 | // if values exceed the encoder scale, fix them (for example: 32002 becomes 00002) 124 | if (smoothAzimuthReading > STEPS_IN_FULL_CIRCLE) 125 | { 126 | smoothAzimuthReading -= STEPS_IN_FULL_CIRCLE; 127 | } 128 | if (smoothAltitudeReading > STEPS_IN_FULL_CIRCLE) 129 | { 130 | smoothAltitudeReading -= STEPS_IN_FULL_CIRCLE; 131 | } 132 | } 133 | }; 134 | 135 | IMU imu; 136 | Encoder myEnc(4, 5); 137 | 138 | void setup() 139 | { 140 | Serial.begin(57600); 141 | Serial.println("\nESP266 boot"); 142 | 143 | Wire.begin(2, 0); // connect D3(GPIO_0) to sensor's SCL, connect D4(GPIO_2) to sensor's SDA 144 | // i2cscan(); // uncomment this if you are having I2C problems (quality of solder contacts, lack of pull-up resistors) 145 | 146 | WiFi.mode(WIFI_AP); 147 | IPAddress ip(1, 2, 3, 4); // The "telescope IP address" that Skysafari should connect to is 1.2.3.4 which is easy to remember. 148 | IPAddress gateway(1, 2, 3, 1); 149 | IPAddress subnet(255, 255, 255, 0); 150 | WiFi.softAPConfig(ip, gateway, subnet); 151 | WiFi.softAP(WiFi_Access_Point_Name); 152 | 153 | IPAddress myIP = WiFi.softAPIP(); 154 | 155 | // Apply compass calibration values: 156 | imu.m_min = (LSM303::vector) {-336, -368, -239}; 157 | imu.m_max = (LSM303::vector) {+363, +316, +367}; 158 | imu.init(); 159 | imu.enableDefault(); 160 | 161 | Serial.println("IMU ok!"); 162 | // tcp listener to receive incoming connections from Skysafari: 163 | server.begin(); 164 | server.setNoDelay(true); 165 | i2cscan(); 166 | } 167 | 168 | long before, after; 169 | void loop() 170 | { 171 | #ifdef TIMEPROFILE 172 | after = millis(); 173 | if (after - before > 0) 174 | { 175 | Serial.print("TIME: "); 176 | Serial.println((after - before)); 177 | } 178 | #endif 179 | attendTcpRequests(); // gets priority to prevent timeouts on Skysafari. Measured AVG execution time = 18ms 180 | yield(); 181 | 182 | if (millis() - last_measurement > MEASUREMENT_PERIOD) // only take new measurements if enough time has elapsed. 183 | { 184 | // Serial.println("reading IMU"); 185 | imu.read(); 186 | // Serial.println("IMU read"); 187 | imu.calculatePosition(); 188 | last_measurement = millis(); 189 | } 190 | 191 | #ifdef TIMEPROFILE 192 | before = millis(); 193 | #endif 194 | yield(); // altough ESP8266 is supposed to do its work background TCP/wiFi after each loop, yieldig here can't hurt 195 | } 196 | 197 | void readEncoder() 198 | { 199 | long newPosition = myEnc.read(); 200 | if (newPosition != oldPosition) 201 | { 202 | oldPosition = newPosition; 203 | Serial.println(newPosition); 204 | } 205 | } 206 | 207 | void attendTcpRequests() 208 | { 209 | // check for new or lost connections: 210 | if (server.hasClient()) 211 | { 212 | Serial.println("hasClient!"); 213 | if (!remoteClient || !remoteClient.connected()) 214 | { 215 | if (remoteClient) 216 | { 217 | Serial.print("Client Disconnected\n"); 218 | remoteClient.stop(); 219 | } 220 | remoteClient = server.available(); 221 | //Serial.print("Inbound connection from: "); 222 | //Serial.println(remoteClient.remoteIP()); 223 | // remoteClient.flush(); 224 | remoteClient.setNoDelay(true); 225 | } 226 | } 227 | 228 | // when we have a new incoming connection from Skysafari: 229 | while (remoteClient.available()) 230 | { 231 | byte skySafariCommand = remoteClient.read(); 232 | 233 | if (skySafariCommand == 81) // 81 is ascii for Q, which is the only command skysafari sends to "basic encoders" 234 | { 235 | char encoderResponse[20]; 236 | //int iAzimuthReading = imu.smoothAzimuthReading; 237 | long iAzimuthReading = myEnc.read(); 238 | int iAltitudeReading = imu.smoothAltitudeReading; 239 | sprintf(encoderResponse, "%i\t%i\t\n", iAzimuthReading, iAltitudeReading); 240 | // Serial.println(encoderResponse); 241 | 242 | remoteClient.println(encoderResponse); 243 | } 244 | else if (skySafariCommand == 72) // 'H' - request for encoder resolution, e.g. 10000-10000\n 245 | { 246 | char response[20]; 247 | // Resolution on both axis is equal 248 | snprintf(response, 20, "%u-%u", STEPS_IN_FULL_CIRCLE, STEPS_IN_FULL_CIRCLE); 249 | // Serial.println(response); 250 | 251 | remoteClient.println(response); 252 | } 253 | else 254 | { 255 | Serial.print("*****"); 256 | Serial.println(skySafariCommand); 257 | } 258 | } 259 | } 260 | 261 | 262 | void i2cscan() 263 | { 264 | byte error, address; 265 | int nDevices; 266 | 267 | Serial.println("Scanning for I2C devices"); 268 | 269 | nDevices = 0; 270 | for (address = 1; address < 127; address++ ) 271 | { 272 | digitalWrite(1, LOW); 273 | // The i2c_scanner uses the return value of the Write.endTransmisstion to see if a device did acknowledge to the address. 274 | Wire.beginTransmission(address); 275 | error = Wire.endTransmission(); 276 | 277 | if (error == 0) 278 | { 279 | digitalWrite(1, HIGH); 280 | Serial.print("I2C device found at address 0x"); 281 | if (address < 16) { Serial.print("0"); } 282 | Serial.print(address, HEX); 283 | Serial.println(" !"); 284 | 285 | delay(5000); 286 | 287 | nDevices++; 288 | } 289 | else if (error == 4) 290 | { 291 | Serial.print("Unknown error at address 0x"); 292 | if (address < 16) {Serial.print("0");} 293 | Serial.println(address, HEX); 294 | } 295 | } 296 | if (nDevices == 0) 297 | { 298 | Serial.println("No I2C devices found\n"); 299 | delay(1000); 300 | } 301 | else 302 | Serial.println("done"); 303 | } 304 | -------------------------------------------------------------------------------- /docs/UploadConfigure.md: -------------------------------------------------------------------------------- 1 | # Uploading and Configuring the DSC Software 2 | 3 | This guide will show you how to upload the DSC software to an ESP-32 microcontroller, and how to configure it for use. 4 | 5 | It is recommended that you complete reading the [Arduino IDE Beginners Guide](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.md) to properly setup your develompent environment before following this guide. 6 | 7 | ## Downloading the DSC code 8 | 9 | Once you have set up your computer with the Arduino IDE+board managers+libraries, use that computer's web browser to open the project GitHub page: [https://github.com/vlaate/DobsonianDSC](https://github.com/vlaate/DobsonianDSC) 10 | 11 | In the web page you'll see a green button called "Code". If you click on it, you will see a menu with an option called "Download ZIP". Click on it! 12 | 13 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/download.png "Downloading Code") 14 | 15 | Your computer will download a compressed file called "DobsonianDSC-master.zip". You need to extract the contents of this file to somewhere you want in your computer. On windows this can be easily done by right-clicking the file and selecting "Extract All". 16 | 17 | After doing this, you should see a folder called "DobsonianDSC-master" in the location you chose to expand the zip file's contents. 18 | 19 | In the extracted contents, locate the following file: 20 | 21 | ``` 22 | DobsonianDSC-master\DobsonianDSC\DobsonianDSC.ino 23 | ``` 24 | 25 | Double click on the file to open it. The Arduino IDE should launch, making the contents of the file visible. 26 | 27 | Plug your USB cable to your ESP32 microcontroller, and plug the other end to your computer. Make sure that your Arduino IDE is still properly configured, as was indicated in the [Arduino IDE Guide](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.md#9-connect-the-microcontroller). 28 | 29 | Follow the steps indicated in [step 13 of the Arduino guide](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.md#13-uploading-the-sketch) to upload the code to the ESP32 board. It will take a while. 30 | 31 | 32 | To validate that the upload was correct, you need to use a WiFi device (laptop, tablet or smartphone) to check the list of WiFi networks around you. 33 | 34 | On the list you should see a new network called "Telescope_DSC". Connect to it. 35 | 36 | Because this is the internal WiFi network of your DSC, it does not have a true connection to the internet. Be aware that some tablets and smartphones, after connecting to a WiFi network that lacks internet connection, will emit a warning like this: 37 | 38 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/WiFi_NoInternet_C.png "WiFi without Internet") 39 | 40 | Make sure to check out these notifications and to tell the device to stay connected to this wifi. Otherwise some phones/tablets disconnect automatically after a few seconds. 41 | 42 | Once you have connected to the DSC's internal WiFi, point your web browser to http://192.168.4.1 43 | 44 | You should see the Web Configuration page: 45 | 46 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/webConfig_sm.png "Web Config") 47 | 48 | ## Configuring the DSC 49 | 50 | In order to change the configuration of the DSC, you need to edit the appropriate fields in the web configuration page, and hit the "Save" button which will store them in permanent memory. For any changes to take effect immediately, you need to hit the "Restart" button, which will reboot the ESP-32. 51 | 52 | ### Encoders and Resolutions 53 | 54 | The fields called *"Azimuth Steps"* and *"Altitude Steps"* can be used to let the DSC know the resolution of it's axis (calculated from encoder resolution and gear ratio), so that it can provide such information to apps like [SkySafari](https://skysafariastronomy.com/). This is quite convenient but optional, since you can also manually configure the resolution information directly in the apps. 55 | 56 | **Flip Azimuth**: Sometimes it happens that after building the DSC electronic circuit and installing the encoders in your telescope, you find out that moving the telescope clockwise in azimuth shows up in skysafari as movement in the opposite direction. To reverse the direction of the Azimuth encoder you could swap the connections of the A and B cables from the Azimuth encoder, but to avoid having to make changes to the hardware, you can just check this *"Flip Azimuth"* option to reverse the direction of the Azimuth encoder if you need to. 57 | 58 | **Flip Altitude**: As with altitude, if you happen to need to reverse the direction of the Altitude encoder, instead of swapping it's A and B cables you can just check this option. 59 | 60 | ### Bluetooth 61 | 62 | The field called *"Bluetooth device Name"* lets you select the name your DSC will use when exposing it's bluetooth interface. Bluetooth is a very convenient way of connecting to the DSC, not just because it's wireless but also because it lets your smarphone/tablet stay connected to the internet. 63 | 64 | After you give your DSC a Bluetooth name, click the *"Save"* button, and later the *"Restart"* button. It may take a while, but you should be able to pair your smartphone or tablet device with it. 65 | 66 | **Note for Android users:** One thing that can be confusing about bluetooth on Android is that after pairing a device, a "connect" button might appear in the bluetooth menu but using it does not seem to do anything. It turns out that the Bluetooth connection needs to be requested by an App (such as Skysafari), and just clicking on "connect" will do nothing unless there is an app requesting bluetooth services. So, after pairing the DSC via bluetooth, to test it with an android device you should use Skysafari or a similar app. 67 | 68 | **Note for iOS users:** It is known that SkySafari on many iOS devices cannot connect to serial devices via Bluetooth. For those phones/tablets the only option is to use WiFi. 69 | 70 | ### Configuring an External WiFi 71 | 72 | When connecting to the DSC via WiFi, we often want to keep having access to the internet in the tablet/smartphone used to connect to it. One way to do this is to make your DSC connect to your existing WiFi, which could be your home's network, or a tethering HotSpot that you open with your smartphone when outside. 73 | 74 | If you fill out the fields labeled *"WiFi SSID Name"* and *"WiFi Password"* and click "Save", where's what will happen: 75 | 76 | Any time the DSC turns on, it will check if there's a value stored for those two fields. When there are no stored values (like when the DSC is brand new), it will launch it's own WiFi called "Telescope-DSC" and take the default IP address (192.168.4.1). 77 | 78 | When there are stored values for WiFi name and password, the DSC will attept to connect to it. If it succeeds then all is well: you can connect to the DSC and to the internet using that WiFi network. If it fails to connect (for example: wrong password, or DSC is used on the field, far away from that WiFi network) then it will resort back to launching its own "Telescope-DSC" WiFi and take the 192.168.4.1 IP. 79 | 80 | #### What's my IP? 81 | 82 | One caveat of having your telescope DSC connect to a home network or other existing WiFi, is that the home's router will assign it a dynamic IP address, and you need a way to figure out this IP address so that you can connect to the DSC. 83 | 84 | To help with that, this DSC software has an optional feature: After it successfully connects to a WiFi that has Internet access, it can send a notification to your smartphone telling you which IP address it was given. 85 | This optional feature depends on an app called PushOver which is available for both Andoid and iOS. It's not free but it's inexpensive (and there's a 30 day trial). 86 | 87 | If you want to use this feature, you'll need to install the PushOver app, create a PushOver account, and login to https://pushover.net/ to do the following steps: 88 | 89 | When logged in to pushover.net, copy your *"user key"* from the top right of the screen: 90 | 91 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/pushover_user_key.png "Pushover user") 92 | 93 | ... and paste it on the DSC web configuration page, to the field labeled *"Notification User Key"*. 94 | 95 | Then in the pushover website, select the option to *"Create an Application/API Token"*: 96 | 97 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/pushover_new_app.png "Pushover Create app") 98 | 99 | ... fill the form (essentially: give the app a name, such as "My Telescope") and click on *"Create Application"*. This will give you a new screen containing an API key for your DSC: 100 | 101 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/pushover_api_key.png "Pushover API key") 102 | 103 | Copy that API key, and paste it on the DSC web configuration page, to the field labeled *"Notification API Key"*. 104 | 105 | If the values are correct, and the PushOver app is properly setup in your smartphone, then everytime the DSC is powered on and connects to an external WiFi, it will send you an notification to your mobile telling you it's IP address, which you will need to use to connect apps like SkySafari to the DSC, and also to acess the web configuration page. 106 | 107 | ## Connecting with SkySafari 108 | 109 | The DSC is meant to be used with astronomy apps that support the "Basic Encoding System" protocol (a.k.a Intelliscope/BBox/Sky Wizard protocol). This guide will assume you are using [SkySafari](https://skysafariastronomy.com/). 110 | 111 | **Note**: only Skysafari Plus and SkySafari Pro support connecting to telescopes, the vanilla "non Plus/Pro" version does not. 112 | 113 | First, you need to make sure that your DSC is powered on and that you can connect to it's web configuration page. 114 | 115 | In SkySafari, tap on the *"Settings"* icon and look for a section called *"Telescope"*. This contains an item called *"Setup"*: 116 | 117 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/Skysafari_3.png "SkySafari B") 118 | 119 | For the *Scope Type*, select *"Basic Encoder System"*, and for the *Mount Type* select *"Alt-Az Push to"* 120 | 121 | Here you should either indicate the values of the encoder steps per revolution, or (if you have configured them in the DSC's web configuration page) check the *"Get Automatically"* box so that SkySafari gets this data directly from the DSC. 122 | 123 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/Skysafari_4.png "SkySafari C") 124 | 125 | You should select either *"Connect via Bluetooth"* or *"Connect via WiFi"*. 126 | 127 | If you select Bluetooth, make sure that Bluetooth is activated in your Smartphone/Tablet and that you have previously paired it with your DSC. 128 | 129 | If you select WiFi, SkySafari will ask you to provide the IP address of the DSC, which is *192.168.4.1* if you are using the DSC's internal WiFi, or is a dynamic IP address if you are using a home/existing WiFi network. Note that the *Port Number* should always be 4030. 130 | 131 | Finally, you can close the Settings menu and in SkySafari's main screen select "Scope" and "Connect". After a second or two, you should hear a "ding" sound, indicating that SkySafari has succesffully connected to the telescope. 132 | 133 | If you don't get the success "ding", check that you can still connect to the web configuration interface. As long as you can browse the web configuration page, you should be able to connect with SkySafari. 134 | 135 | If you haven't yet built the circuit for the DSC, now is the right time to check the [Guide for Building the Circuit](https://github.com/vlaate/DobsonianDSC/blob/master/docs/Solderless.md). 136 | 137 | ### Alignment Tips 138 | 139 | It's often recommended that you first use a bubble level to properly level the base of your dobsonian, to get more accuracy from the DSC. 140 | 141 | Once SkySafari successfully connects to your DSC, it will initially assume that the telescope is pointing due north. You need to do the *Star Alignment* in order to get the DSC and SkySafari to properly display where your telescope is aimed at. 142 | 143 | What you need to do is point the telescope at a known star, and center the star in the field of view of the eyepiece. In Skysafari, search and select the same star, and tap *"Align"*. Now the SkySafari screen should be showing where your telescope is aimed at, as you move it. 144 | 145 | SkySafari now also lets you perform a second alignment with another star (hopefully one that's far away from the first star). This is called 2-star alignment, and it reduces the need to level the telescope mount base, and improves accuracy. 146 | 147 | To do this, just repeat the alignment process with a second star, choosing *"Align"* rather than *"Restart Alignment"* when asked by SkySafari. Doing this second star alignment will allow SkySafari to have a better model of the mount and more accurately display where your telescope is pointed at. 148 | 149 | **Note:** Although it seems that you can align on more stars, SkySafari only remembers the two most recent alignments. 150 | 151 | The first time you do this process, you might discover at this point that when moving the telescope in one direction, SkySafari shows it moving in the opposite direction. To fix this, go to the DSC's web configuration page, and change the **Flip Azimuth** and **Flip Altitude** options to reverse the direction of either encoder. 152 | -------------------------------------------------------------------------------- /DobsonianDSC/DobsonianDSC.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Dual Encoder Digital Setting Circles for ESP32 microcontrollers 3 | 4 | This is a simple adapter to connect two inexpensive incremental optical encoders 5 | via WiFi and/or BlueTooth to astronomy apps such as SkySafari, 6 | in order to provide "Push To" style features to dobsonian style telescope mounts. 7 | 8 | Copyright (c) 2017-2021 Vladimir Atehortúa. All rights reserved. 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the version 3 GNU General Public License as 12 | published by the Free Software Foundation. 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | You should have received a copy of the GNU General Public License along with this program. 18 | If not, see 19 | */ 20 | 21 | /** 22 | Hardware used: 23 | ESP-32S Development Board, I used a generic one: https://www.aliexpress.com/item/32800930387.html? 24 | Two Optical Encoders with NPN (a.k.a. "N channel") "Open Collector" (a.k.a. "open drain") output, I used some 600 p/r generic ones: https://www.aliexpress.com/item/32669741048.html 25 | Dupont cables or other suitable connectors. 26 | Power supply (could be a 5V USB power bank) 27 | 28 | ESP32 pinout: 29 | IMPORTANT: Only encoders with NPN Open collector outputs can be connected directly to the pins of the ESP32. Other encoders might require level shifters. 30 | AZ_Encoder_A = GPIO18 = D18 (or change it in the #define code below) 31 | AZ_Encoder_B = GPIO19 = D19 (or change it in the #define code below) 32 | ALT_Encoder_A = GPIO25 = D25 (or change it in the #define code below) 33 | ALT_Encoder_B = GPIO26 = D26 (or change it in the #define code below) 34 | VIN = Encoder VCC (and if you don't use a USB power bank, this would also connect to your 5V power source) 35 | GND = Encoder GND 36 | */ 37 | 38 | /** 39 | Required Additional Board Managers for the Arduino IDE (File->Preferences): 40 | https://dl.espressif.com/dl/package_esp32_index.json 41 | 42 | Required libraries for the Arduino IDE (Tools -> Manage Libraries): 43 | ESP32Encoder by Kevin Harrington, see https://github.com/madhephaestus/ESP32Encoder 44 | ArduinoJson by Benoit Blanchon, see https://arduinojson.org/ 45 | */ 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include "WebConfig_DSC.h" 53 | #include "BluetoothSerial.h" 54 | #if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) 55 | #error Bluetooth is not enabled! Please run `make menuconfig` to and enable it 56 | #endif 57 | 58 | #define MAX_SRV_CLIENTS 3 // How many clients can connect simultaneously to the DSC. 59 | 60 | // Choose which pins of the ESP32 to use for the Azimuth Encoder 61 | ESP32Encoder AZencoder; 62 | #define PIN_AZ_A 18 // Important: Not all encoders can be connected directly to the pins of the ESP32, read more about this in the project's github page 63 | #define PIN_AZ_B 19 64 | 65 | // Choose which pins of the ESP32 to use for the Altitude Encoder 66 | ESP32Encoder ALTencoder; 67 | #define PIN_ALT_A 25 68 | #define PIN_ALT_B 26 69 | 70 | BluetoothSerial SerialBT; 71 | 72 | String params = "[" 73 | "{" 74 | "'name':'ssid'," 75 | "'label':'WiFi SSID Name'," 76 | "'type':" + String(INPUTTEXT) + "," 77 | "'default':''" 78 | "}," 79 | "{" 80 | "'name':'pwd'," 81 | "'label':'WiFi Password'," 82 | "'type':" + String(INPUTPASSWORD) + "," 83 | "'default':''" 84 | "}," 85 | "{" 86 | "'name':'btname'," 87 | "'label':'Bluetooth device Name'," 88 | "'type':" + String(INPUTTEXT) + "," 89 | "'default':'Telescope'" 90 | "}," 91 | "{" 92 | "'name':'azsteps'," 93 | "'label':'Azimuth Steps'," 94 | "'type':" + String(INPUTNUMBER) + "," 95 | "'min':1,'max':10000000," 96 | "'default':'10000'" 97 | "}," 98 | "{" 99 | "'name':'alsteps'," 100 | "'label':'Altitude Steps'," 101 | "'type':" + String(INPUTNUMBER) + "," 102 | "'min':1,'max':10000000," 103 | "'default':'10000'" 104 | "}," 105 | "{" 106 | "'name':'flpaz'," 107 | "'label':'Flip Azimuth'," 108 | "'type':" + String(INPUTCHECKBOX) + "," 109 | "'default':'1'" 110 | "}," 111 | "{" 112 | "'name':'flpalt'," 113 | "'label':'Flip Altitude'," 114 | "'type':" + String(INPUTCHECKBOX) + "," 115 | "'default':'1'" 116 | "}," 117 | "{" 118 | "'name':'apikey'," 119 | "'label':'Notification API Key (optional)'," 120 | "'type':" + String(INPUTTEXT) + "," 121 | "'max':35," 122 | "'default':''" 123 | "}," 124 | "{" 125 | "'name':'userkey'," 126 | "'label':'Notification User Key (optional)'," 127 | "'type':" + String(INPUTTEXT) + "," 128 | "'max':35," 129 | "'default':''" 130 | "}" 131 | "]"; 132 | 133 | WebServer server; 134 | WiFiServer TCPserver(4030); // 4030 is the TCP port Skysafari usually expects for "Basic Encoder Systems" 135 | WiFiClient serverClients[MAX_SRV_CLIENTS]; 136 | WebConfig conf; 137 | 138 | boolean initWiFi() { 139 | boolean connected = false; 140 | WiFi.mode(WIFI_STA); 141 | Serial.print("Connecting to "); 142 | Serial.print(conf.values[0]); 143 | Serial.println(" SSID"); 144 | if (conf.values[0] != "") { 145 | WiFi.begin(conf.values[0].c_str(), conf.values[1].c_str()); 146 | uint8_t cnt = 0; 147 | while ((WiFi.status() != WL_CONNECTED) && (cnt < 20)) { 148 | delay(500); 149 | Serial.print("."); 150 | cnt++; 151 | } 152 | Serial.println(); 153 | if (WiFi.status() == WL_CONNECTED) { 154 | Serial.print("IP Adress:"); 155 | Serial.println(WiFi.localIP()); 156 | connected = true; 157 | // We can use the Pushover service to send a notification to your smartphone, an easy way to obtain the IP address that was assigned to the device: 158 | if (conf.getValue("apikey") != "" && conf.getValue("userkey") != "") 159 | { 160 | HTTPClient http; 161 | http.begin("https://api.pushover.net/1/messages.json"); 162 | http.addHeader("Content-Type", "application/x-www-form-urlencoded"); 163 | char _header[300]; 164 | sprintf(_header, "token=%s&user=%s&message=Telescope-DSC connected to SSID:[%s] IP Address: [%s]", conf.getValue("apikey"), conf.getValue("userkey"), WiFi.SSID(), WiFi.localIP().toString().c_str()); 165 | int httpResponseCode = http.POST(_header); 166 | http.end(); 167 | } 168 | } 169 | } 170 | if (!connected) { // if unable to connect to an external WiFi, let's launch our own passwordless WiFi SSID: 171 | WiFi.mode(WIFI_AP); 172 | WiFi.softAP("Telescope_DSC", "", 1); 173 | } 174 | return connected; 175 | } 176 | 177 | // method that handles the clicks on "SAVE" and "RESET" buttons on the web config form 178 | void handleRoot() { 179 | conf.handleFormRequest(&server); 180 | if (server.hasArg("SAVE")) 181 | { 182 | uint8_t cnt = conf.getCount(); 183 | for (uint8_t i = 0; i < cnt; i++) 184 | { 185 | Serial.print(conf.getName(i)); 186 | Serial.print(" = "); 187 | Serial.println(conf.values[i]); 188 | } 189 | } 190 | } 191 | 192 | /** 193 | Configure encoders using the ESP32Encoder library by Kevin Harrington. 194 | This uses the ESP32's hardware pulse counters, not polling nor interrupts! 195 | In my tests, two encoders can get simultaneous quadrature pulses at 20Khz without missing a step. 196 | */ 197 | void setupEncoders() 198 | { 199 | // It's very important to enable pull up resistors in order to work with the NPN open collector outputs: 200 | ESP32Encoder::useInternalWeakPullResistors = UP; 201 | 202 | // flip the pins used by Altitude encoder if the web configuration says so: 203 | if (conf.getBool("flpalt")) 204 | { 205 | ALTencoder.attachFullQuad(PIN_ALT_A, PIN_ALT_B); 206 | } 207 | else 208 | { 209 | ALTencoder.attachFullQuad(PIN_ALT_B, PIN_ALT_A); 210 | } 211 | ALTencoder.setFilter(0); // filters are only needed for cheap mechanical/switch encoders such as the Keyes KY-040, not by the 600p/s encoders 212 | 213 | // flip the pins used by Azimuth encoder if the web configuration says so: 214 | if (conf.getBool("flpaz")) 215 | { 216 | AZencoder.attachFullQuad(PIN_AZ_B, PIN_AZ_A); 217 | } 218 | else 219 | { 220 | AZencoder.attachFullQuad(PIN_AZ_A, PIN_AZ_B); 221 | } 222 | AZencoder.setFilter(0); // filters are only needed for cheap mechanical/switch encoders such as the Keyes KY-040, not by the 600p/s encoders 223 | 224 | // set starting count value after attaching 225 | ALTencoder.clearCount(); 226 | AZencoder.clearCount(); 227 | } 228 | 229 | void attendTcpRequests() // handle connections from SkySafari and similar software via TCP on port 4030 230 | { 231 | uint8_t i; 232 | // check for new clients trying to connect 233 | if (TCPserver.hasClient()) { 234 | for (i = 0; i < MAX_SRV_CLIENTS; i++) { 235 | // find a free or disconnected spot: 236 | if (!serverClients[i] || !serverClients[i].connected()) { 237 | if (serverClients[i]) { 238 | serverClients[i].stop(); 239 | } 240 | serverClients[i] = TCPserver.available(); 241 | // the new client has connected 242 | break; 243 | } 244 | } 245 | // when there are no free/disconnected spots, reject the new client: 246 | if (i == MAX_SRV_CLIENTS) { 247 | WiFiClient serverClient = TCPserver.available(); 248 | serverClient.stop(); 249 | } 250 | } 251 | 252 | // check what the client sends for "Basic Encoder protocol" commands: 253 | for (i = 0; i < MAX_SRV_CLIENTS; i++) 254 | { 255 | if (serverClients[i] && serverClients[i].connected()) 256 | { 257 | if (serverClients[i].available()) 258 | { 259 | char character = serverClients[i].read(); // read the first character received, usually the command 260 | char response[30]; 261 | switch (character) 262 | { 263 | case 'Q': // the Query command, sent by SkySafari and others as the "Basic Encoder protocol" to query for encoder values. 264 | sprintf(response, "%lld\t%lld\t\n", AZencoder.getCount(), ALTencoder.getCount()); 265 | serverClients[i].println(response); 266 | break; 267 | case 'H': // 'H' - request for encoder resolution, e.g. 10000-10000\n 268 | snprintf(response, 20, "%u-%u", conf.getInt("azsteps"), conf.getInt("alsteps")); 269 | serverClients[i].println(response); 270 | break; 271 | } 272 | } 273 | } 274 | } 275 | } 276 | 277 | void attendBTRequests() // handle connections from SkySafari and similar software via BlueTooth 278 | { 279 | if (SerialBT.available()) { 280 | //WiFi.disconnect(); 281 | //WiFi.mode(WIFI_OFF); 282 | 283 | char character = SerialBT.read(); 284 | while (SerialBT.available()) 285 | { 286 | SerialBT.read(); // flushing the buffer was necessary with Skysafari 5 Plus because it sent an initial "long" command. 287 | } 288 | Serial.print("BT: "); 289 | Serial.println(character); 290 | char response[30]; 291 | switch (character) 292 | { 293 | case 'Q': // the Query command, sent by SkySafari and others as the "Basic Encoder protocol" to query for encoder values. 294 | sprintf(response, "%lld\t%lld\t\n", AZencoder.getCount(), ALTencoder.getCount()); 295 | Serial.print(response); 296 | SerialBT.println(response); 297 | break; 298 | case 'H': // 'H' - request for encoder resolution, e.g. 10000-10000\n 299 | snprintf(response, 20, "%u-%u\n", conf.getInt("azsteps"), conf.getInt("alsteps")); 300 | Serial.print(response); 301 | SerialBT.println(response); 302 | break; 303 | default: SerialBT.print("\n"); 304 | } 305 | } 306 | } 307 | 308 | /** 309 | * Arduino main setup method 310 | */ 311 | void setup() { 312 | Serial.begin(115200); 313 | Serial.println(params); 314 | conf.setDescription(params); 315 | conf.readConfig(); 316 | initWiFi(); 317 | char dns[30]; 318 | sprintf(dns, "%s.local", conf.getApName()); 319 | if (MDNS.begin(dns)) 320 | { 321 | Serial.println("DNS active"); 322 | } 323 | server.on("/", handleRoot); 324 | server.begin(80); // starts web server for configuration page 325 | 326 | TCPserver.begin(); // starts TCP server for skysafari via WiFi 327 | 328 | setupEncoders(); 329 | 330 | if (conf.getValue("btname") != "") // starts Bluetooth service only if a name has been configured for our device 331 | { 332 | SerialBT.begin(conf.getValue("btname")); 333 | Serial.println("BT started"); 334 | } 335 | } 336 | 337 | void loop() 338 | { 339 | server.handleClient(); 340 | attendBTRequests(); 341 | attendTcpRequests(); 342 | } 343 | -------------------------------------------------------------------------------- /docs/UploadConfigure.es.md: -------------------------------------------------------------------------------- 1 | # Cargando y Configurando el Apuntador Digital 2 | [[English]](https://github.com/vlaate/DobsonianDSC/blob/master/docs/UploadConfigure.md) 3 | [[Español]](https://github.com/vlaate/DobsonianDSC/blob/master/docs/UploadConfigure.es.md) 4 | 5 | La presente guía le mostrará cómo cargar el código del apuntador a un microcontrolador ESP32, cómo configurarlo y cómo conectar la aplicación móvil SkySafari Plus/Pro al apuntador. 6 | 7 | Se recomienda que antes de seguir ésta guía, usted primero complete los pasos de la [Guía de Arduino para Principiantes](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.es.md) en la cual se indica cómo configurar correctamente Arduino en su computador incluyendo las placas y librerías que el proyecto necesita. 8 | 9 | ## Descargando el Código del Apuntador 10 | 11 | Una vez usted haya terminado la configuración del IDE Arduino (y las placas y librerías del proyecto) en su computador, abra en el navegador del computador la página GitHub del proyecto: [https://github.com/vlaate/DobsonianDSC](https://github.com/vlaate/DobsonianDSC) 12 | 13 | En la página encontrará un botón verde llamado "Code" o “Código”. Si hace click en éste, verá un menú con una opción llamada "Download ZIP" o “Descargar ZIP”. Haga click en ese botón! 14 | 15 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/download.png "Downloading Code") 16 | 17 | Su computador descargará un archivo comprimido llamado *"DobsonianDSC-master.zip"*. Usted necesita extraer los contenidos de ese archivo y grabarlos en el lugar que desee en su computador. En Windows ésto se logra haciendo click derecho en el archivo y seleccionando "Extract All" (“Extraer Todo”). 18 | 19 | Después de hacer ésto, usted debería tener una carpeta llamada "DobsonianDSC-master" en su computador, en la ubicación que eligió para descomprimir el archivo ZIP. 20 | 21 | En dicha carpeta, encuentre el siguiente archivo: 22 | 23 | ``` 24 | DobsonianDSC-master\DobsonianDSC\DobsonianDSC.ino 25 | ``` 26 | 27 | Haga doble click en dicho archivo para abrirlo. Ello debería lanzar el IDE Arduino, haciendo visibles los contenidos del archivo. 28 | 29 | Conecte el cable USB a su microcontrolador ESP32, y el otro extremo a su computador. Asegúrese de que su IDE Arduino esté apropiadamente configurada como se indicó en el [paso #9 de la Guía de Arduino para Principiantes](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.es.md#9-conectar-el-microcontrolador). 30 | 31 | Siga los pasos indicados en el [paso #13 de la Guía de Arduino](https://github.com/vlaate/DobsonianDSC/blob/master/docs/ArduinoIDE.es.md#13-cargando-el-programa) para cargar el código al dispositivo ESP32. Ésto tomará varios minutos. 32 | 33 | Una vez cargado el programa en el ESP32, para validar que funcione correctamente es necesario usar un dispositivo WiFi (computador portátil, tableta o celular) para revisar la lista de redes WiFi a su alrededor. 34 | 35 | En la lista debería aparecer una nueva red llamada "Telescope_DSC". Conéctese a esa red. 36 | 37 | Como ésta es la red WiFi interna creada por el Apuntador Digital, es una red que no tiene verdadera conexión a Internet. Tenga presente que algunas tabletas y celulares al conectarse a una red WiFi que no tiene internet, pueden emitir advertencias como la siguiente: 38 | 39 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/WiFi_NoInternet_C.png "WiFi without Internet") 40 | 41 | Asegúrese de revisar esas notificaciones y de decirle al celular/tableta que siga conectado a éste WiFi. De no hacerlo, algunos celulares/tabletas se desconectan automáticamente después de unos segundos. 42 | 43 | Una vez usted se ha conectado al WiFi interno del dispositivo, apunte su navegador hacia http://192.168.4.1 44 | 45 | Eso debería abrirle la página web de configuración del Apuntador Digital: 46 | 47 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/webConfig_sm.png "Web Config") 48 | 49 | ## Configurando el Apuntador Digital 50 | 51 | Para cambiar la configuración del ADT, usted necesita editar los campos apropiados en la página web de configuración, y presionar el botón "Save" par que queden grabados permanentemente. Para que los cambios tengan efecto inmediato, es necesario presionar el botón "Restart", el cual hará que el ESP-32 se reinicie. 52 | 53 | ### Encoders y Resoluciones 54 | 55 | Los campos llamados *"Azimuth Steps"* y *"Altitude Steps"* pueden ser usados para permitirle al ADT saber la resolución de los ejes del telescopio (derivada de los Encoders y de la relación de transmisión), de manera que el dispositivo pueda darle dicha información a apps como [SkySafari](https://skysafariastronomy.com/). 56 | 57 | **Flip Azimuth**: Algunas veces pasa que al ensamblar el ADT e instalar los encoders en el telescopio, uno encuentra que al mover horizontalmente el telescopio en el sentido de las manecillas del reloj, la pantalla de SkySafari muestra el movimiento pero en la dirección opuesta. Para reversar la dirección del Encoder Azimutal sin tener que hacer cambios físicos al circuito, simplemente use ésta opción de *"Flip Azimuth"*. 58 | 59 | **Flip Altitude**: De igual manera, si usted necesita reversar la dirección de movimiento del Encoder de Altitud, puede simplemente usar ésta opción. 60 | 61 | ### Bluetooth 62 | 63 | El campo llamado *"Bluetooth device Name"* le permite definir el nombre que su ADT usará para exponer su interfaz bluetooth. Bluetooth es un mecanismo muy conveniente para conectarse al ADT sin usar cables, que además permite que su celular/tableta siga conectada a internet. 64 | 65 | Después de darle un nombre Bluetooth a su dispositivo, haga click en el botón *"Save"*, y luego en el botón *"Restart"*. Puede tomar un par de minutos, pero con eso ya debería poder emparejar su teléfono o tableta con el dispositivo ADT. 66 | 67 | **Nota para usuarios Android:** Algo que puede ser confuso al usar bluetooth en Android es que después de emparejar un dispositivo, puede aparecer un botón de “conectar” en el menú bluetooth, pero usar ese botón no parece hacer nada. Lo que ocurre es que la conexión Bluetooth debe ser solicitada por una App (como SkySafari), y el sólo hacer click en "conectar" no es suficiente. Entonces, después de emparejar su ADT por bluetooth, para probarlo en un celular o tableta Android es necesario usar una app como SkySafari. 68 | 69 | **Nota para usuarios iOS:** Es conocido que en iOS no es posible conectar SkySafari con dispositivos seriales a través de Bluetooth. En esos teléfonos/tabletas la única forma de conectarse con el ADT es usando WiFi. 70 | 71 | ### Configurando un WiFi externo 72 | 73 | Cuando se usa una conexión WiFi para conectarse al ADT, puede ocurrir que queramos mantener una conexión a internet en la tableta o teléfono usado para conectarnos al ADT. Una forma de lograrlo es hacer que el ADT se conecte a una red WiFi existente, que puede ser la red de nuestra casa o un HotSpot (tethering) que abrimos en el celular. 74 | 75 | En la página web de configuración, si llenamos los campos llamados *"WiFi SSID Name"* y *"WiFi Password"* y hacemos click en "Save", lo que ocurre es lo siguiente: 76 | 77 | Cada vez que el ADT es encendido, revisará si hay algún valor asignado para éstos dos campos. Cuando no existen valores (por ejemplo cuando el ADT está nuevo), el dispositivo lanzará su propia red WiFi llamada "Telescope-DSC" y tomará la dirección IP por defecto (192.168.4.1). 78 | 79 | Cuando por el contrario, si habían valores almacenados para los dos campos en cuestión, el ADT intentará conectarse a esa red. Si lo logra, todo estará bien: usted puede conectarse al ADT y al internet al mismo tiempo usando esa red WiFi. Si el ADT no logra conectarse a esa red WiFi (por ejemplo: la contraseña es incorrecta, o el ADT se usa muy lejos de esa red) entonces el ADT recurrirá por defecto a lanzar su propia red WiFi llamada "Telescope-DSC" y tomar la IP 192.168.4.1. 80 | 81 | #### Cual es mi dirección IP? 82 | 83 | Una complicación de hacer que el ADT se conecte a una red WiFi existente, es que esa red le asignará una dirección IP dinámica, y usted necesita una forma de averiguar esa dirección IP para poderse conectar al ADT. 84 | 85 | Como ayuda, éste sistema ADT tiene una funcionalidad opcional: Después de conectarse exitosamente a una red WiFi que tiene acceso a internet, el dispositivo puede enviarte una notificación a tu celular diciéndote la dirección IP que le asignaron. 86 | 87 | Ésta funcionalidad opcional depende de una app llamada PushOver, la cual está disponible tanto para Android como para iOS. No es gratuita, pero es poco costosa (y tiene un período de prueba de 30 días). 88 | 89 | Si desea usar ésta funcionalidad, necesitará instalar la app PushOver en su celular, crear una cuenta PushOver, y autenticarse en https://pushover.net/ para realizar los siguientes pasos: 90 | 91 | Estando autenticado en pushover.net, copie su *"user key"* de la parte superior derecha de la pantalla: 92 | 93 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/pushover_user_key.png "Pushover user") 94 | 95 | ... y pegue ese valor en la página web de configuración del ADT, en el campo llamado *"Notification User Key"*. 96 | 97 | Luego, en la página de pushover, seleccione la opción de *"Create an Application/API Token"*: 98 | 99 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/pushover_new_app.png "Pushover Create app") 100 | 101 | ... llene el formulario (esencialmente: darle un nombre y descripción, como "Mi Telescopio") y haga click en *"Create Application"*. Ésto abrirá una nueva pantalla que contiene el *API key* para usar: 102 | 103 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/pushover_api_key.png "Pushover API key") 104 | 105 | Copie ese *API key*, y péguelo en la página web de configuración del ADT, en el campo llamado *"Notification API Key"*. 106 | 107 | Si los valores son correctos, y la app de PushOver está correctamente instalada en su celular, entonces cada vez que el DSC sea encendido y se conecte a un WiFi externo, el dispositivo le enviará una notificación a su celular indicando la dirección IP que le asignó la red, que es la dirección IP que necesitará usar para conectar SkySafari y para abrir la página web de configuración del ADT. 108 | 109 | ## Conectando SkySafari 110 | 111 | El ADT está concebido para ser usado con aplicaciones de astronomía que soportan el protocolo "Basic Encoding System" (también llamado protocolo Intelliscope/BBox/Sky Wizard). La presente guía recomienda usar [SkySafari](https://skysafariastronomy.com/) y muestra los ejemplos con esa app. 112 | 113 | **Nota**: Sólo las versiones **SkySafari Plus** y **SkySafari Pro** soportan conectarse al ADT. La versión sencilla (que no es “Plus” ni “Pro”) no sirve para el proyecto. 114 | 115 | Primero, usted necesita asegurarse de que su ADT esté encendido y de que puede conectarse a la página web de configuración (usando su navegador web). 116 | 117 | En SkySafari, seleccione el ícono de *"Settings"* y busque la sección llamada *"Telescope"*. Ésta contiene un ítem llamado *"Setup"*: 118 | 119 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/Skysafari_3.png "SkySafari B") 120 | 121 | Para el campo *Scope Type*, seleccione *"Basic Encoder System"*, y para el campo *Mount Type* seleccione *"Alt-Az Push to"* 122 | 123 | Aquí es necesario indicar los valores de “encoder steps per revolution” que es la resolución de cada eje del telescopio. También puede seleccionar *"Get Automatically"* si ya los cargó en la página web de configuración del ADT. 124 | 125 | ![alt text](https://raw.githubusercontent.com/vlaate/DobsonianDSC/master/img/Skysafari_4.png "SkySafari C") 126 | 127 | Es necesario indicarle a SkySafari si la conexión con el ADT debe ser por Bluetooth o por WiFi: 128 | 129 | Si selecciona Bluetooth, asegúrese de que el celular/tableta si tenga activado el bluetooth, y de que previamente haya sido emparejado con el ADT. 130 | 131 | Si selecciona WiFi, SkySafari le pedirá indicar la dirección IP del ADT, la cual es *192.168.4.1* si está usando el WiFi interno del ADT, ó es una dirección IP dinámica si está usando una red WiFi ya existente. Recuerde que el número de puerto (*Port Number*) siempre deberá ser 4030. 132 | 133 | Finalmente, puede cerrar el menú de Settings para regresar a la pantalla principal de SkySafari (que tiene un mapa del cielo). En esa pantalla, seleccione el botón "Scope" y luego el botón "Connect". Luego de un par de segundos debería escuchar un agudo "ding", sonido que indica que SkySafari se ha conectado exitosamente al telescopio. 134 | 135 | Si en lugar de escuchar el exitoso "ding" aparece un mensaje de error, verifique que aún pueda abrir la página web de configuración del ADT en el mismo celular/tableta y revise la configuración. 136 | 137 | Habiendo cargado y configurado el microcontrolador ESP32 con el sistema ADT, ahora es el momento oportuno para seguir la [Guía para Armar el Circuito del ADT](https://github.com/vlaate/DobsonianDSC/blob/master/docs/Solderless.es.md). 138 | 139 | ### Recomendaciones de Alineación 140 | 141 | Siempre que vaya a usar el ADT, se recomienda usar un nivel (de los que tienen una burbuja en un líquido brillante) para nivelar horizontalmente la base del telescopio dobsoniano y así obtener mayor precisión del ADT. 142 | 143 | Cuando abra SkySafari y lo conecte exitosamente al ADT, la app no sabrá inicialmente a dónde está apuntando el telescopio. Es necesario realizar un proceso de Alineación (*Star Alignment*) para poder que la pantalla de SkySafari presente correctamente hacia dónde está apuntando el telescopio. 144 | 145 | Para hacer esa alineación, lo que hay que hacer es apuntar el telescopio hacia una estrella conocida y centrar la estrella en el campo visual del ocular. Entonces, en SkySafari busque y seleccione la misma estrella y presione el botón *"Align"*. Con eso, la pantalla de SkySafari debería mostrar correctamente hacia dónde está apuntando el telescopio, a medida que usted lo mueve. 146 | 147 | SkySafari ahora también permite realizar una segunda alineación con una segunda estrella (ojalá una que esté en otra parte alejada del cielo). Esto se llama *“2-star alignment”*, y reduce la necesidad de nivelar la base del telescopio antes de usarlo y aumenta la precisión del ADT. 148 | 149 | Para hacer la segunda alineación, simplemente repita el proceso con una segunda estrella conocida, asegurándose de presionar *"Align"* (no *"Restart Alignment"*) cuando SkySafari lo pregunte. Hacer éste segundo alineamiento le permite a SkySafari tener un mejor modelo matemático del telescopio y así presentar un despliegue más preciso de su posición. 150 | 151 | **Nota:** Aunque pareciera que puede hacerse la alineación con más estrellas, en realidad SkySafari sólo recuerda las últimas dos estrellas usadas para la alineación. 152 | 153 | La primera vez que usted realice éste proceso, podría ocurrir que encuentra que al mover el telescopio en una dirección, SkySafari muestra el movimiento en la dirección opuesta. Para corregir ésto, abra la página web de configuración del ADT y cambie las opciones **Flip Azimuth** y/o **Flip Altitude** para reversar la dirección del encoder que necesite corregir. 154 | -------------------------------------------------------------------------------- /DobsonianDSC/WebConfig_DSC.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Original file: WebConfig.cpp version 1.4 4 | Author Gerald Lechner lechge@gmail.com https://github.com/GerLech 5 | Licensed under the GNU Lesser General Public License v3.0 6 | 7 | This library builds a web page with a smart phone friendly form to edit 8 | a free definable number of configuration parameters. The submitted data will bestored in the SPIFFS 9 | The library works with ESP8266 and ESP32 10 | 11 | It has been modified by Vladimir Atehortúa to customize the HTML styles for 12 | astronomy/night use (dark background with red colors) for the Digital Setting Circles project 13 | which is released under the compatible GPLv3 14 | 15 | This program is free software: you can redistribute it and/or modify 16 | it under the terms of the version 3 GNU General Public License as 17 | published by the Free Software Foundation. 18 | This program is distributed in the hope that it will be useful, 19 | but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | GNU General Public License for more details. 22 | You should have received a copy of the GNU General Public License along with this program. 23 | If not, see 24 | */ 25 | 26 | #include "WebConfig_DSC.h" 27 | #include 28 | #if defined(ESP32) 29 | #include "SPIFFS.h" 30 | #include 31 | #else 32 | #include 33 | #endif 34 | #include 35 | #include 36 | 37 | const char * inputtypes[] = {"text","password","number","date","time","range","check","radio","select","color","float"}; 38 | 39 | //HTML templates 40 | //Template for header and begin of form 41 | const char HTML_START[] PROGMEM = 42 | "\n" 43 | "\n" 44 | "\n" 45 | "\n" 46 | "\n" 47 | "Telescope-DSC\n" 48 | "\n" 87 | "\n" 88 | "\n" 89 | "\n" 90 | "
\n" 91 | "
Telescope DSC
\n" 92 | "
\n"; 93 | 94 | //Template for one input field 95 | const char HTML_ENTRY_SIMPLE[] PROGMEM = 96 | "
%s
\n" 97 | "
\n"; 98 | const char HTML_ENTRY_AREA[] PROGMEM = 99 | "
%s
\n" 100 | "
\n"; 101 | const char HTML_ENTRY_NUMBER[] PROGMEM = 102 | "
%s
\n" 103 | "
\n"; 104 | const char HTML_ENTRY_RANGE[] PROGMEM = 105 | "
%s
\n" 106 | "
%i  %i
\n"; 107 | const char HTML_ENTRY_CHECKBOX[] PROGMEM = 108 | "
%s
\n"; 109 | const char HTML_ENTRY_RADIO_TITLE[] PROGMEM = 110 | "
%s
\n"; 111 | const char HTML_ENTRY_RADIO[] = 112 | "
%s
\n"; 113 | const char HTML_ENTRY_SELECT_START[] PROGMEM = 114 | "
%s
\n" 115 | "
\n"; 120 | const char HTML_ENTRY_MULTI_START[] PROGMEM = 121 | "
%s
\n" 122 | "
\n"; 123 | const char HTML_ENTRY_MULTI_OPTION[] PROGMEM = 124 | " %s
\n"; 125 | const char HTML_ENTRY_MULTI_END[] PROGMEM = 126 | "
\n"; 127 | 128 | //Template for save button and end of the form with save 129 | const char HTML_END[] PROGMEM = 130 | "
\n" 131 | "
\n" 132 | "
\n" 133 | "
\n" 134 | "\n" 135 | "\n"; 136 | //Template for save button and end of the form without save 137 | const char HTML_BUTTON[] PROGMEM = 138 | "\n"; 139 | 140 | WebConfig::WebConfig() { 141 | _apName = ""; 142 | }; 143 | 144 | void WebConfig::setDescription(String parameter){ 145 | _count = 0; 146 | addDescription(parameter); 147 | } 148 | 149 | void WebConfig::addDescription(String parameter){ 150 | DeserializationError error; 151 | const int capacity = JSON_ARRAY_SIZE(MAXVALUES) 152 | + MAXVALUES*JSON_OBJECT_SIZE(8); 153 | DynamicJsonDocument doc(capacity); 154 | char tmp[40]; 155 | error = deserializeJson(doc,parameter); 156 | if (error ) { 157 | Serial.println(parameter); 158 | Serial.print("JSON AddDescription: "); 159 | Serial.println(error.c_str()); 160 | } else { 161 | JsonArray array = doc.as(); 162 | uint8_t j = 0; 163 | for (JsonObject obj : array) { 164 | if (_count()) { 170 | uint8_t t = 0; 171 | strlcpy(tmp,obj["type"],30); 172 | while ((tINPUTTYPES) t = 0; 174 | _description[_count].type = t; 175 | } else { 176 | _description[_count].type = obj["type"]; 177 | } 178 | } else { 179 | _description[_count].type = INPUTTEXT; 180 | } 181 | _description[_count].max = (obj.containsKey("max"))?obj["max"] :100; 182 | _description[_count].min = (obj.containsKey("min"))?obj["min"] : 0; 183 | if (obj.containsKey("default")) { 184 | strlcpy(tmp,obj["default"],30); 185 | values[_count] = String(tmp); 186 | } else { 187 | values[_count]="0"; 188 | } 189 | if (obj.containsKey("options")) { 190 | JsonArray opt = obj["options"].as(); 191 | j = 0; 192 | for (JsonObject o : opt) { 193 | if (j(); 195 | _description[_count].labels[j] = o["l"].as(); 196 | } 197 | j++; 198 | } 199 | _description[_count].optionCnt = opt.size(); 200 | } 201 | } 202 | _count++; 203 | } 204 | 205 | } 206 | _apName = WiFi.macAddress(); 207 | _apName.replace(":",""); 208 | if (!SPIFFS.begin()) { 209 | SPIFFS.format(); 210 | SPIFFS.begin(); 211 | } 212 | }; 213 | 214 | void createSimple(char * buf, const char * name, const char * label, const char * type, String value) { 215 | sprintf(buf,HTML_ENTRY_SIMPLE,label,type,value.c_str(),name); 216 | } 217 | 218 | void createTextarea(char * buf, DESCRIPTION descr, String value) { 219 | //max = rows min = cols 220 | sprintf(buf,HTML_ENTRY_AREA,descr.label,descr.max,descr.min,descr.name, value.c_str()); 221 | } 222 | 223 | void createNumber(char * buf, DESCRIPTION descr, String value) { 224 | sprintf(buf,HTML_ENTRY_NUMBER,descr.label,descr.min,descr.max, value.c_str(),descr.name); 225 | } 226 | 227 | void createRange(char * buf, DESCRIPTION descr, String value) { 228 | sprintf(buf,HTML_ENTRY_RANGE,descr.label,descr.min,descr.min,descr.max,value.c_str(),descr.name,descr.max); 229 | } 230 | 231 | void createCheckbox(char * buf , DESCRIPTION descr, String value) { 232 | if (value != "0") { 233 | sprintf(buf,HTML_ENTRY_CHECKBOX,descr.label,"checked",descr.name); 234 | } else { 235 | sprintf(buf,HTML_ENTRY_CHECKBOX,descr.label,"",descr.name); 236 | } 237 | } 238 | 239 | void createRadio(char * buf , DESCRIPTION descr, String value, uint8_t index) { 240 | if (value == descr.options[index]) { 241 | sprintf(buf,HTML_ENTRY_RADIO,descr.name,descr.options[index].c_str(),"checked",descr.labels[index].c_str()); 242 | } else { 243 | sprintf(buf,HTML_ENTRY_RADIO,descr.name,descr.options[index].c_str(),"",descr.labels[index].c_str()); 244 | } 245 | } 246 | 247 | void startSelect(char * buf , DESCRIPTION descr) { 248 | sprintf(buf,HTML_ENTRY_SELECT_START,descr.label,descr.name); 249 | } 250 | 251 | void addSelectOption(char * buf, String option, String label, String value) { 252 | if (option == value) { 253 | sprintf(buf,HTML_ENTRY_SELECT_OPTION,option.c_str(),"selected",label.c_str()); 254 | } else { 255 | sprintf(buf,HTML_ENTRY_SELECT_OPTION,option.c_str(),"",label.c_str()); 256 | } 257 | } 258 | 259 | void startMulti(char * buf , DESCRIPTION descr) { 260 | sprintf(buf,HTML_ENTRY_MULTI_START,descr.label); 261 | } 262 | 263 | void addMultiOption(char * buf, String name, uint8_t option, String label, String value) { 264 | if ((value.length() > option) && (value[option] == '1')) { 265 | sprintf(buf,HTML_ENTRY_MULTI_OPTION,name.c_str(),option,"checked",label.c_str()); 266 | } else { 267 | sprintf(buf,HTML_ENTRY_MULTI_OPTION,name.c_str(),option,"",label.c_str()); 268 | } 269 | } 270 | 271 | //***********Different type for ESP32 WebServer and ESP8266WebServer ******** 272 | //both classes have the same functions 273 | #if defined(ESP32) 274 | //function to respond a HTTP request for the form use the default file 275 | //to save and restart ESP after saving the new config 276 | void WebConfig::handleFormRequest(WebServer * server){ 277 | handleFormRequest(server,CONFFILE); 278 | } 279 | //function to respond a HTTP request for the form use the filename 280 | //to save. If auto is true restart ESP after saving the new config 281 | void WebConfig::handleFormRequest(WebServer * server, const char * filename){ 282 | #else 283 | //function to respond a HTTP request for the form use the default file 284 | //to save and restart ESP after saving the new config 285 | void WebConfig::handleFormRequest(ESP8266WebServer * server){ 286 | handleFormRequest(server,CONFFILE); 287 | } 288 | //function to respond a HTTP request for the form use the filename 289 | //to save. If auto is true restart ESP after saving the new config 290 | void WebConfig::handleFormRequest(ESP8266WebServer * server, const char * filename){ 291 | #endif 292 | //******************** Rest of the function has no difference *************** 293 | uint8_t a,v; 294 | String val; 295 | if (server->args() > 0) { 296 | if (server->hasArg(F("apName"))) _apName = server->arg(F("apName")); 297 | for (uint8_t i= 0; i < _count; i++) { 298 | if (_description[i].type == INPUTCHECKBOX) { 299 | values[i] = "0"; 300 | if (server->hasArg(_description[i].name)) values[i] = "1"; 301 | } else if (_description[i].type == INPUTMULTICHECK) { 302 | values[i]=""; 303 | for (a=0; a<_description[i].optionCnt; a++) values[i] += "0"; //clear result 304 | for (a=0; a< server->args(); a++) { 305 | if (server->argName(a) == _description[i].name) { 306 | val = server->arg(a); 307 | v= val.toInt(); 308 | values[i].setCharAt(v,'1'); 309 | } 310 | } 311 | } else { 312 | if (server->hasArg(_description[i].name)) values[i] = server->arg(_description[i].name) ; 313 | } 314 | } 315 | if (server->hasArg(F("SAVE")) || server->hasArg(F("RST"))) { 316 | writeConfig(filename); 317 | if (server->hasArg(F("RST"))) ESP.restart(); 318 | } 319 | } 320 | boolean exit = false; 321 | if (server->hasArg(F("SAVE")) && _onSave) { 322 | _onSave(getResults()); 323 | exit = true; 324 | } 325 | if (server->hasArg(F("DONE")) && _onDone) { 326 | _onDone(getResults()); 327 | exit = true; 328 | } 329 | if (server->hasArg(F("CANCEL")) && _onCancel) { 330 | _onCancel(); 331 | exit = true; 332 | } 333 | if (server->hasArg(F("DELETE")) && _onDelete) { 334 | _onDelete(_apName); 335 | exit = true; 336 | } 337 | if (!exit) { 338 | server->setContentLength(CONTENT_LENGTH_UNKNOWN); 339 | sprintf(_buf,HTML_START,_apName.c_str()); 340 | server->send(200, "text/html", _buf); 341 | // if (_buttons == BTN_CONFIG) { 342 | // createSimple(_buf,"apName","Name for Internal Access Point","text",_apName); 343 | // server->sendContent(_buf); 344 | // } 345 | 346 | for (uint8_t i = 0; i<_count; i++) { 347 | switch (_description[i].type) { 348 | case INPUTFLOAT: 349 | case INPUTTEXT: createSimple(_buf,_description[i].name,_description[i].label,"text",values[i]); 350 | break; 351 | case INPUTTEXTAREA: createTextarea(_buf,_description[i],values[i]); 352 | break; 353 | case INPUTPASSWORD: createSimple(_buf,_description[i].name,_description[i].label,"password",values[i]); 354 | break; 355 | case INPUTDATE: createSimple(_buf,_description[i].name,_description[i].label,"date",values[i]); 356 | break; 357 | case INPUTTIME: createSimple(_buf,_description[i].name,_description[i].label,"time",values[i]); 358 | break; 359 | case INPUTCOLOR: createSimple(_buf,_description[i].name,_description[i].label,"color",values[i]); 360 | break; 361 | case INPUTNUMBER: createNumber(_buf,_description[i],values[i]); 362 | break; 363 | case INPUTRANGE: createRange(_buf,_description[i],values[i]); 364 | break; 365 | case INPUTCHECKBOX: createCheckbox(_buf,_description[i],values[i]); 366 | break; 367 | case INPUTRADIO: sprintf(_buf,HTML_ENTRY_RADIO_TITLE,_description[i].label); 368 | for (uint8_t j = 0 ; j<_description[i].optionCnt; j++) { 369 | server->sendContent(_buf); 370 | createRadio(_buf,_description[i],values[i],j); 371 | } 372 | break; 373 | case INPUTSELECT: startSelect(_buf,_description[i]); 374 | for (uint8_t j = 0 ; j<_description[i].optionCnt; j++) { 375 | server->sendContent(_buf); 376 | addSelectOption(_buf,_description[i].options[j],_description[i].labels[j],values[i]); 377 | } 378 | server->sendContent(_buf); 379 | strcpy_P(_buf,HTML_ENTRY_SELECT_END); 380 | break; 381 | case INPUTMULTICHECK: startMulti(_buf,_description[i]); 382 | for (uint8_t j = 0 ; j<_description[i].optionCnt; j++) { 383 | server->sendContent(_buf); 384 | addMultiOption(_buf,_description[i].name,j,_description[i].labels[j],values[i]); 385 | } 386 | server->sendContent(_buf); 387 | strcpy_P(_buf,HTML_ENTRY_MULTI_END); 388 | break; 389 | default : _buf[0] = 0; 390 | break; 391 | 392 | } 393 | server->sendContent(_buf); 394 | } 395 | if (_buttons == BTN_CONFIG) { 396 | server->sendContent(HTML_END); 397 | } else { 398 | server->sendContent("
\n"); 399 | if ((_buttons & BTN_DONE) == BTN_DONE) { 400 | sprintf(_buf,HTML_BUTTON,"DONE","Done"); 401 | server->sendContent(_buf); 402 | } 403 | if ((_buttons & BTN_CANCEL) == BTN_CANCEL) { 404 | sprintf(_buf,HTML_BUTTON,"CANCEL","Cancel"); 405 | server->sendContent(_buf); 406 | } 407 | if ((_buttons & BTN_DELETE) == BTN_DELETE) { 408 | sprintf(_buf,HTML_BUTTON,"DELETE","Delete"); 409 | server->sendContent(_buf); 410 | } 411 | server->sendContent("
\n"); 412 | } 413 | } 414 | } 415 | //get the index for a value by parameter name 416 | int16_t WebConfig::getIndex(const char * name){ 417 | int16_t i = _count-1; 418 | while ((i>=0) && (strcmp(name,_description[i].name)!=0)) { 419 | i--; 420 | } 421 | return i; 422 | } 423 | //read configuration from file 424 | boolean WebConfig::readConfig(const char * filename){ 425 | String line,name,value; 426 | uint8_t pos; 427 | int16_t index; 428 | if (!SPIFFS.exists(filename)) { 429 | //if configfile does not exist write default values 430 | writeConfig(filename); 431 | } 432 | File f = SPIFFS.open(filename,"r"); 433 | if (f) { 434 | Serial.println(F("Read configuration")); 435 | uint32_t size = f.size(); 436 | while (f.position() < size) { 437 | line = f.readStringUntil(10); 438 | pos = line.indexOf('='); 439 | name = line.substring(0,pos); 440 | value = line.substring(pos+1); 441 | if ((name == "apName") && (value != "")) { 442 | _apName = value; 443 | Serial.println(line); 444 | } else { 445 | index = getIndex(name.c_str()); 446 | if (!(index < 0)) { 447 | value.replace("~","\n"); 448 | values[index] = value; 449 | if (_description[index].type == INPUTPASSWORD) { 450 | Serial.printf("%s=*************\n",_description[index].name); 451 | } else { 452 | Serial.println(line); 453 | } 454 | } 455 | } 456 | } 457 | f.close(); 458 | return true; 459 | } else { 460 | Serial.println(F("Cannot read configuration")); 461 | return false; 462 | } 463 | } 464 | //read configuration from default file 465 | boolean WebConfig::readConfig(){ 466 | return readConfig(CONFFILE); 467 | } 468 | //write configuration to file 469 | boolean WebConfig::writeConfig(const char * filename){ 470 | String val; 471 | File f = SPIFFS.open(filename,"w"); 472 | if (f) { 473 | f.printf("apName=%s\n",_apName.c_str()); 474 | for (uint8_t i = 0; i<_count; i++){ 475 | val = values[i]; 476 | val.replace("\n","~"); 477 | f.printf("%s=%s\n",_description[i].name,val.c_str()); 478 | } 479 | return true; 480 | } else { 481 | Serial.println(F("Cannot write configuration")); 482 | return false; 483 | } 484 | 485 | } 486 | //write configuration to default file 487 | boolean WebConfig::writeConfig(){ 488 | return writeConfig(CONFFILE); 489 | } 490 | //delete configuration file 491 | boolean WebConfig::deleteConfig(const char * filename){ 492 | return remove(filename); 493 | } 494 | //delete default configutation file 495 | boolean WebConfig::deleteConfig(){ 496 | return deleteConfig(CONFFILE); 497 | } 498 | 499 | //get a parameter value by its name 500 | const String WebConfig::getString(const char * name) { 501 | int16_t index; 502 | index = getIndex(name); 503 | if (index < 0) { 504 | return ""; 505 | } else { 506 | return values[index]; 507 | } 508 | } 509 | 510 | 511 | //Get results as a JSON string 512 | String WebConfig::getResults(){ 513 | char buffer[1024]; 514 | StaticJsonDocument<1000> doc; 515 | for (uint8_t i = 0; i<_count; i++) { 516 | switch (_description[i].type) { 517 | case INPUTPASSWORD : 518 | case INPUTSELECT : 519 | case INPUTDATE : 520 | case INPUTTIME : 521 | case INPUTRADIO : 522 | case INPUTCOLOR : 523 | case INPUTTEXT : doc[_description[i].name] = values[i]; break; 524 | case INPUTCHECKBOX : 525 | case INPUTRANGE : 526 | case INPUTNUMBER : doc[_description[i].name] = values[i].toInt(); break; 527 | case INPUTFLOAT : doc[_description[i].name] = values[i].toFloat(); break; 528 | 529 | } 530 | } 531 | serializeJson(doc,buffer); 532 | return String(buffer); 533 | } 534 | 535 | //Ser values from a JSON string 536 | void WebConfig::setValues(String json){ 537 | int val; 538 | float fval; 539 | char sval[255]; 540 | DeserializationError error; 541 | StaticJsonDocument<1000> doc; 542 | error = deserializeJson(doc, json); 543 | if (error ) { 544 | Serial.print("JSON: "); 545 | Serial.println(error.c_str()); 546 | } else { 547 | for (uint8_t i = 0; i<_count; i++) { 548 | if (doc.containsKey(_description[i].name)){ 549 | switch (_description[i].type) { 550 | case INPUTPASSWORD : 551 | case INPUTSELECT : 552 | case INPUTDATE : 553 | case INPUTTIME : 554 | case INPUTRADIO : 555 | case INPUTCOLOR : 556 | case INPUTTEXT : strlcpy(sval,doc[_description[i].name],255); 557 | values[i] = String(sval); break; 558 | case INPUTCHECKBOX : 559 | case INPUTRANGE : 560 | case INPUTNUMBER : val = doc[_description[i].name]; 561 | values[i] = String(val); break; 562 | case INPUTFLOAT : fval = doc[_description[i].name]; 563 | values[i] = String(fval); break; 564 | 565 | } 566 | } 567 | } 568 | } 569 | } 570 | 571 | 572 | const char * WebConfig::getValue(const char * name){ 573 | int16_t index; 574 | index = getIndex(name); 575 | if (index < 0) { 576 | return ""; 577 | } else { 578 | return values[index].c_str(); 579 | } 580 | } 581 | 582 | int WebConfig::getInt(const char * name){ 583 | return getString(name).toInt(); 584 | } 585 | 586 | float WebConfig::getFloat(const char * name){ 587 | return getString(name).toFloat(); 588 | } 589 | 590 | boolean WebConfig::getBool(const char * name){ 591 | return (getString(name) != "0"); 592 | } 593 | 594 | //get the accesspoint name 595 | const char * WebConfig::getApName(){ 596 | return _apName.c_str(); 597 | } 598 | //get the number of parameters 599 | uint8_t WebConfig::getCount(){ 600 | return _count; 601 | } 602 | 603 | //get the name of a parameter 604 | String WebConfig::getName(uint8_t index){ 605 | if (index < _count) { 606 | return String(_description[index].name); 607 | } else { 608 | return ""; 609 | } 610 | } 611 | 612 | //set the value for a parameter 613 | void WebConfig::setValue(const char*name,String value){ 614 | int16_t i = getIndex(name); 615 | if (i>=0) values[i] = value; 616 | } 617 | 618 | //set the label for a parameter 619 | void WebConfig::setLabel(const char * name, const char* label){ 620 | int16_t i = getIndex(name); 621 | if (i>=0) strlcpy(_description[i].label,label,LABELLENGTH); 622 | } 623 | 624 | //remove all options 625 | void WebConfig::clearOptions(uint8_t index){ 626 | if (index < _count) _description[index].optionCnt = 0; 627 | } 628 | 629 | void WebConfig::clearOptions(const char * name){ 630 | int16_t i = getIndex(name); 631 | if (i >= 0) clearOptions(i); 632 | } 633 | 634 | //add a new option 635 | void WebConfig::addOption(uint8_t index, String option){ 636 | addOption(index,option,option); 637 | } 638 | 639 | void WebConfig::addOption(uint8_t index, String option, String label){ 640 | if (index < _count) { 641 | if (_description[index].optionCnt < MAXOPTIONS) { 642 | _description[index].options[_description[index].optionCnt]=option; 643 | _description[index].labels[_description[index].optionCnt]=label; 644 | _description[index].optionCnt++; 645 | } 646 | } 647 | } 648 | 649 | //modify an option 650 | void WebConfig::setOption(uint8_t index, uint8_t option_index, String option, String label){ 651 | if (index < _count) { 652 | if (option_index < _description[index].optionCnt) { 653 | _description[index].options[option_index] = option; 654 | _description[index].labels[option_index] = label; 655 | } 656 | } 657 | 658 | } 659 | 660 | void WebConfig::setOption(char * name, uint8_t option_index, String option, String label){ 661 | int16_t i = getIndex(name); 662 | if (i >= 0) setOption(i,option_index,option,label); 663 | } 664 | 665 | //get the options count 666 | uint8_t WebConfig::getOptionCount(uint8_t index){ 667 | if (index < _count) { 668 | return _description[index].optionCnt; 669 | } else { 670 | return 0; 671 | } 672 | } 673 | 674 | uint8_t WebConfig::getOptionCount(char * name){ 675 | int16_t i = getIndex(name); 676 | if (i >= 0) { 677 | return getOptionCount(i); 678 | } else { 679 | return 0; 680 | } 681 | 682 | } 683 | 684 | //set form type to doen cancel 685 | void WebConfig::setButtons(uint8_t buttons){ 686 | _buttons = buttons; 687 | } 688 | //register onSave callback 689 | void WebConfig::registerOnSave(void (*callback)(String results)){ 690 | _onSave = callback; 691 | } 692 | //register onSave callback 693 | void WebConfig::registerOnDone(void (*callback)(String results)){ 694 | _onDone = callback; 695 | } 696 | //register onSave callback 697 | void WebConfig::registerOnCancel(void (*callback)()){ 698 | _onCancel = callback; 699 | } 700 | //register onDelete callback 701 | void WebConfig::registerOnDelete(void (*callback)(String name)){ 702 | _onDelete = callback; 703 | } 704 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------