Developing for Kstars

To build a stable or latest version of kstars, I recommend using the script from https://gitea.nouspiro.space/nou/astro-soft-build

The script creates two folder trees: one for the source code and another one for the build folders, that will include all the compiled objects and artifacts.

Once the script has created the build folder structure, you can manually build other parts of the application, like custom drivers or a new feature you would like to contribute.

I use the script to bootstrap the development and thus having a working copy of the app.

You can modify the original scripts and use your own repository or branch. Copy the original scripts and fine tune them for your repository and branch.

If you modify a driver, make sure to also edit file $INDIROOT/drivers.xml to sync the version.

Use make help to get a list of all available targets

You can also manually rebuild parts of the components. Here there are some tips.

Create build folder structure

You first need to create the building folder structure. This is the way the scripts create the build structure, so you can also make your own customization.

$ [ ! -d ../build-indi ] && \
 { cmake -B ../build-indi ../indi -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release || \
{ echo "INDI configuration failed"; exit 1; } }
$ cd ../build-indi

Workflow for testing a new version of a driver

In order to run your own version of a driver, you need to first build it. Here are the steps I follow:

  • Make your changes on the corresponding code.
  • Build the driver from the build folder.
  • Copy the driver to the driver install folder.

Use make help to get a list of all available targets in case of doubt.

For example, to build SnapCap driver:

$ cd ~/astro-soft-stable/build-indi/drivers/auxiliary

$ make indi_snapcap
[  0%] Built target hid
[  9%] Built target indicore
[ 22%] Built target indidevice
[ 22%] Built target eventloop
[ 31%] Built target dsp
[ 36%] Built target fpack
[ 95%] Built target indidriver_OBJECT
[ 95%] Built target indidriver
[100%] Built target indi_snapcap

To build a single driver:

  1. go to the build-* driver folder
  2. run make
  3. to install run sudo make install

For example, to build Rolloffino driver on the indi-3rdparties repository:

$ cd ~/astro-soft-stable/build-indi-3rdparty/indi-rolloffino 
$ make
Scanning dependencies of target indi_rolloffino
Building CXX object indi-rolloffino/CMakeFiles/indi_rolloffino.dir/rolloffino.cpp.o
Linking CXX executable indi_rolloffino
Built target indi_rolloffino

To install it you can use sudo make install or manually copy the driver to the driver folder (at /usr/bin for a RaspberryPi).

Start indiserver

It is better to manually start indiserver so you can see logs and other debugging information. For example, this will start a server listing on port 7624 and starting two drivers: Snapcap and Simultar filter wheel.

/usr/bin/indiserver -v -p 7624 -m 2048 -r 0 indi_snapcap  indi_simulator_wheel

Once you manually started the server, you can run kstars and create a profile to connect to the indiserver, in this case you should consider it a remote server running on your local computer. The profile should include the devices you used to manually start indiserver, and it is mandatory to include a CCD. In this example, the devices are Filter Simulator, SnapCap and the mandatory CCD, in this case, the Simulator.

Ejemplo de servidor TCP ESP8266

Hay montones de ejemplos de servidor TCP utilizando ESP8266WiFi. Están pensados para hacer peticiones sin estado, al estilo del protocolo HTTP. En este tipo de peticiones, el cliente abre la conexión, envía la petición, recibe la respuesta y luego cierra la conexión, por eso se dice que no tienen estado. Aqui hay un ejemplo de este código, para un servidor HTTP, que es un servidor sin estado (stateless).

El modelo de programación consiste en realizar la gestión de la transacción en el bloque loop del programa. En dicho bucle, se crea y se destruye el objeto cliente. Eso supone que el cliente tiene que volver a abrir la conexión en la siguiente conexión.

Este modelo no es adecuado para clientes que abren la conexión una vez y esperan que el servidor no la cierre. Un ejemplo de este tipo de conexiones es el de los drivers de indilib.

Si necesitas crear un servidor TCP que no cierre la conexión, es conveniente organizar el codigo de otra forma.

Aqui comparto una solución de ejemplo de código.

/*
   SocketServer.ino

    Created on: 4.11.2023
    Author: miceno.atreides@gmail.com

*/

#include <ESP8266WiFi.h>

#define MAX_BAUD_RATE 115200
String strData = "";             // a string to hold incoming data

uint16_t port = 8888;            // Port number
WiFiServer server(port);

// WiFi parameters
const char* ssid = "MOVISTAR_3887";
const char* password = "r5PP867mGjxKX329YhSj";
WiFiClient client;

void setup() {
  Serial.begin(MAX_BAUD_RATE);
  while (Serial.available() > 0)
    ;

  WiFi.disconnect();  //Prevent connecting to wifi based on previous configuration
  ESP.eraseConfig();

  WiFi.mode(WIFI_STA);  // WiFi mode station (connect to wifi router only)

  // Connect to WiFi
  WiFi.begin(ssid, password);
  Serial.println();
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(200);
  }
  Serial.println("\nWiFi connected");
  Serial.print("START MIRRORING SERIAL: ");
  Serial.println(WiFi.localIP());

  // Start server
  server.begin();
}

void loop() {
  // Serial.println("looping..."); // DEBUG
  if (!client){
    // Only request a client in case there is no valid one.
    // The client should persist on the loop so it is a global variable.
    client = server.available();
  }

  if (client) {
    // Serial.println("client...");
    while (client.available() > 0) {
      char c;
      // Serial.println("data is available...");
      // read data from the connected client
      c = client.read();
      strData += c;
    }

    // Serial.println("\nNo more data available");
    if( strData.length()>0){
      Serial.printf("Message received is %s\n", strData.c_str());
      client.write("Response received");
      client.flush();
      strData = "";
    }
  }
}

La clave está en el código marcado:

if (!client){
    // Only request a client in case there is no valid one.
    // The client should persist on the loop so it is a global variable.
    client = server.available();
  }

En este caso, solamente debemos obtener un objeto WiFiClient si no tenemos uno disponible anteriormente. Se trata de obtener uno cuando sea necesario o cuando no esté disponible el anterior. Además, debemos asegurarnos de no llamar a client.stop() en el propio bucle, porque si no, cerraríamos la conexión y el cliente tendría que volver a abrirla.

Este patron de desarrollo se basa en la sobrecarga del operador bool en la clase WiFiClient.

Para verificar que esta solucion es robusta, puedes cargar el sketch anterior en un ESP8266, y por otro lado, desde un PC conectado a la misma WiFi, puedes construir un programa en Python para hacer de cliente. El programa Python abre la conexión con el servidor una única vez y envia continuamente peticiones.

De esta forma hemos construido un servidor TCP persistente, utilizando las librerías básicas de ESP8266. También puedes mejorar el codigo utilizando una librería asíncrona como ESPAsyncTCP.