Visualizzazione post con etichetta GoLang. Mostra tutti i post
Visualizzazione post con etichetta GoLang. Mostra tutti i post

giovedì 8 agosto 2024

Proiezione isometrica Esercizio GoNum Golang

 Per esercizio ho provato ad implementare la proiezione isometrica in GO partendo dalle istruzioni su Wikipedia


i calcoli matriciali sono i seguenti (in questo caso e' gia' impostata una rotazione di alfa di circa 35 gradi e beta di 45 gradi





package main
import (
"fmt"
"math"
"gonum.org/v1/gonum/mat"
)
func format(matrix mat.Matrix) {
formatted := mat.Formatted(matrix, mat.Prefix(""), mat.Squeeze())
fmt.Println(formatted)
}


func isometrica(x,y,z float64) (float64,float64) {
xs := mat.NewDense(3, 3, []float64{math.Sqrt(3), 0, -math.Sqrt(3),
1, 2, 1,
math.Sqrt(2),  -math.Sqrt(2),  math.Sqrt(2)})
//format(xs)
w := mat.NewDense(3, 3,nil)
w.Scale(1/math.Sqrt(6),xs)
coordinate := mat.NewVecDense(3, []float64{x,y,z}) // e' gia' un vettore colonna
c := mat.NewVecDense(3,  make([]float64, 3))
c.MulVec(w,coordinate)
pr := mat.NewDense(3, 3, []float64{1,0,0,0,1,0,0,0,1})
d := mat.NewVecDense(3,  make([]float64, 3))
d.MulVec(pr,c)
return d.AtVec(1),d.AtVec(2)
}
func main() {
xi,yi := isometrica(0.0,0.0,0.0)
fmt.Printf("data: %f,%f\n", xi,yi)
xi,yi = isometrica(1.0,0.0,0.0)
fmt.Printf("data: %f,%f\n", xi,yi)
xi,yi = isometrica(0.0,1.0,0.0)
fmt.Printf("data: %f,%f\n", xi,yi)
xi,yi = isometrica(1.0,1.0,0.0)
fmt.Printf("data: %f,%f\n", xi,yi)
xi,yi = isometrica(0.0,0.0,2.0)
fmt.Printf("data: %f,%f\n", xi,yi)
xi,yi = isometrica(1.0,0.0,2.0)
fmt.Printf("data: %f,%f\n", xi,yi)
xi,yi = isometrica(0.0,1.0,2.0)
fmt.Printf("data: %f,%f\n", xi,yi)
xi,yi = isometrica(1.0,1.0,2.0)
fmt.Printf("data: %f,%f\n", xi,yi)

}


venerdì 3 novembre 2023

Golang Milk-V Duo RiscV64

Sto provando la scheda Milk-D Duo con processore RiscV64

La scheda un processore dual core. Sul core primario puo' girare un sistema operativo Linux multi task mentre sul secondo FreeRtos. I due sistema comunicano tramite una porta seriale un po' come funzionava la scheda Intel Galileo

Come immagine disco sto usando quella della ditta ma si pu' montare anche Arch (Debian non ha ancora il supporto di rete e si deve accedere tramite connessione seriale)

Su Linux e' piuttosto facile interagire con la scheda perche' se si collega la porta USB C viene montata in automatico un porta di rete tramite RNDIS all'indirizzo 192.168.42.1 (la scheda rilascia in DHCP un indirizzo della classe 192.168.42.x)

enxf611f7e3617d: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.42.18  netmask 255.255.255.0  broadcast 192.168.42.255
        inet6 fe80::f411:f7ff:fee3:617d  prefixlen 64  scopeid 0x20<link>
        ether f6:11:f7:e3:61:7d  txqueuelen 1000  (Ethernet)
        RX packets 242  bytes 29688 (28.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 311  bytes 43373 (42.3 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisi


ssh root@192.168.42.1


https://xyzdims.com/3d-printers/misc-hardware-notes/iot-milk-v-duo-risc-v-esbc-running-linux/#References 


La scheda ha 64 Mb di memoria ma con il disco immagine la RAM e' di soli 32 Mb perche' gli altri sono destinati alla connessione della video camera

Mem: 20732K used, 8504K free, 80K shrd, 668K buff, 3260K cached
CPU:   0% usr   1% sys   0% nic  98% idle   0% io   0% irq   0% sirq
Load average: 1.90 0.55 0.19 1/64 385
  PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
  374   368 root     R     1200   4%   0% top
  155     1 root     S     4740  16%   0% /usr/sbin/ntpd -g -p /var/run/ntpd.pid
  193     1 root     S     1200   4%   0% {S99user} /bin/sh /etc/init.d/S99user start
  361   163 root     S     1008   3%   0% /usr/sbin/dropbear -R
   10     2 root     IW       0   0%   0% [rcu_preempt]
  204     1 root     S     1204   4%   0% -sh

Per usare Go pero' tale memoria non e' sufficiente e si deve attivare la partizione di swap tramite per avere 256 Mb

mkswap /dev/mmcblk0p3
swapon /dev/mmcblk0p3

la dimensione della partizione di swap puo' essere ridimensionata tramite lo script (in questo caso e' 1 Giga)
 
fdisk /dev/mmcblk0 <<EOF
p
d
3
n
p
3

+1G

w
EOF

fdisk -l /dev/mmcblk0

echo "----- resize /dev/mmcblk0p3 for swap completed -----"


a questo punto si puo' fare la build del programma sul desktop facendo cross compilazione
export GOOS=linux 
export GOARCH=riscv64 
export CGO_ENABLED=1 
export CC=riscv64-linux-gnu-gcc
go build programma.go

e si copia sul dispositivo tramite (attenzione allo switch -O)

Ho fatto qualche prova e non sembrano funzionare le goroutines

Nel caso il cui si voglia esporre la scheda verso Internet si puo' usare o la scheda di espansione che monta una scheda Ethernet



oppure se il PC Desktop assume un IP del tipo 192.168.42.68 si lancia sul desktop

sysctl net.ipv4.ip_forward=1 
iptables -P FORWARD ACCEPT 
iptables -t nat -A POSTROUTING -o outgoing_if -j MASQUERADE

mentre sulla scheda

ip r add default via 192.168.42.68

martedì 17 ottobre 2023

Small docker for Golang apps

 In ufficio oramai una applicazione non puo' andare in produzione se non e' in in container...anche se e' occupa pochissimo spazio disco...il che vuol dire che si deve cercare di ottimizzare l'occupazione dello spazio disco del container

Nel Dockerfile sottostante viene utilizzato un docker con compilatore per generare un eseguibile che sara' poi copiato in un docker minimale di produzione basato su Alpine



FROM golang as builder
RUN mkdir /app
COPY . /app
WORKDIR /app
RUN https_proxy=http://proxy.toscana.it:8080 go get github.com/go-resty/resty/v2
RUN https_proxy=http://proxy.toscana.it:8080 go get github.com/sijms/go-ora/v2

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o myapp .

FROM alpine:latest

RUN apk --no-cache add ca-certificates
WORKDIR /root
COPY --from=builder /app/myapp .

venerdì 16 giugno 2023

Restful con resty in Go

Attenzione : necesssita' di GO 1.20. In versioni precedenti viene generato l'errore value.SetZero undefined


La libreria Resty github.com/go-resty/resty/ permette di utilizzare servizi restful anche dietro a proxy e con connessione insecure (connessione https con certificato scaduto) 

=========================================================

package main
import (
"crypto/tls"
"fmt"
"os"
"github.com/go-resty/resty/v2"   // libreria per rest
)
type AuthSuccess struct {
success      string
version      string
JWT          string
username     string
userid       string
parentuserid string
userlevelid  string
}
func dieOnError(msg string, err error) {
if err != nil {
fmt.Println(msg, err)
os.Exit(1)
}
}
// go get github.com/go-resty/resty/v2
func main() {
connStr := "connessione"
conn, err := go_ora.NewConnection(connStr)
dieOnError("Can't create connection:", err)
err = conn.Open()
dieOnError("Can't open connection:", err)
client := resty.New()
client.SetProxy("http://proxy.xxxxx.toscana.it:8080")
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
// User Login
resp, err := client.R().
SetFormData(map[string]string{
"username": "admin",
"password": "xxxxxxx",
"action":   "login",
}).
SetResult(&AuthSuccess{}).
Post("https://xxxxxx.xxxxxxx.it/api/")
if err != nil {
fmt.Println("Errore di login")
os.Exit(1)
}
token := resp.Result().(*AuthSuccess).JWT
fmt.Print(token)
}

Connession Oracle DB con Go

Per provare a connettere Oracle tramite Go torna utile utilizzare il seguente docker

docker run -d -p 1521:1521 -e ORACLE_PASSWORD=<your password> -v oracle-volume:/opt/oracle/oradata gvenzl/oracle-xe

l'utente e' SYSTEM e service XEPDB1

Per interagire con il db si puo' usare SQLDeveloper  

Con la libreria go-ora non e' necessario installare le librerie client di Oracle


package main

import (
    "database/sql"
    "fmt"

    _ "github.com/sijms/go-ora/v2"
)

func sqlOperations(db *sql.DB) {
    var queryResultColumnOne string
    row := db.QueryRow("SELECT punto_cantiere FROM nodo")
    err := row.Scan(&queryResultColumnOne)
    if err != nil {
        panic(fmt.Errorf("error scanning query result from database into target variable: %w", err))
    }
    fmt.Println("Risultato query", queryResultColumnOne)

    rows, err := db.Query("SELECT punto_cantiere,valore FROM nodo")
    if err != nil {
        // handle this error better than this
        panic(err)
    }
    defer rows.Close()

    for rows.Next() {
        var numero float32
        var nome string
        err = rows.Scan(&nome, &numero)
        if err != nil {
            // handle this error
            panic(err)
        }
        fmt.Println(numero, nome)
    }
    // get any error encountered during iteration
    err = rows.Err()
    if err != nil {
        panic(err)
    }

}

func GetSqlDBWithPureDriver(dbParams map[string]string) *sql.DB {
    connectionString := "oracle://" + dbParams["username"] + ":" +
        dbParams["password"] + "@" + dbParams["server"] + ":" +
        dbParams["port"] + "/" + dbParams["service"]
    db, err := sql.Open("oracle", connectionString)
    if err != nil {
        panic(fmt.Errorf("error in sql.Open: %w", err))
    }
    err = db.Ping()
    if err != nil {
        panic(fmt.Errorf("error pinging db: %w", err))
    }
    return db
}

var localDB = map[string]string{
    "service":  "XEPDB1",
    "username": "system",
    "server":   "localhost",
    "port":     "1521",
    "password": "password",
}

func main() {
    db := GetSqlDBWithPureDriver(localDB)
    defer func() {
        err := db.Close()
        if err != nil {
            fmt.Println("Can't close connection: ", err)
        }
    }()
    sqlOperations(db)
}

mercoledì 31 maggio 2023

Batimetria Venezia da Landsat con Decision Tree

Visto lo scarso successo del metodo analitico visto qui ho provato un metodo alternativo citato spesso il letteratura che prevede la determinazione della batimetria da dati ottici usando la rete neurale decision tree

Per cambiare ho preso i dati Landsat sulla Laguna di Venezia (la batimetria della Laguna di Venezia e' molto piu' variabile rispetto a quella della lagunan di Orbetello) usando come dato di verita' a terra questo geotiff http://cigno.ve.ismar.cnr.it/layers/geonode%3Alag02_okart_export

Su Qgis sono stati importate le bande RGB,NIR di Landsat8  e dopo avere creato una griglia regolare di punti spaziati di 30 m e' stata estratta una tabella con i valori di riflettanza per ogni banda e la profondita'


 Una volta ottenuto il file CSV i dati di batimetria sono stati divisi in classi di 0.5 m per rendere possibile la successiva elaborazione tramite un semplice script in GO 


package main

import (
    "encoding/csv"
    "fmt"
    "io"
    "log"
    "math"
    "os"
    "strconv"
)

func main() {
    f, err := os.Open("tree.csv")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    csvReader := csv.NewReader(f)
    for {
        rec, err := csvReader.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            log.Fatal(err)
        }
        tt, _ := strconv.ParseFloat(rec[0], 'f')
        t := math.Abs(tt)
        fmt.Print(math.Floor(t / 0.5))
        fmt.Println("," + rec[1] + "," + rec[2] + "," + rec[3] + "," + rec[4])

    }
}


il file finale ha un formato del tipo

 classe,red,nir,gren,blue
3,6707,5566,8241,9397
3,6714,5575,8221,9375
3,6696,5573,8184,9369
3,6665,5577,8144,9331
3,6638,5584,8089,9287
3,6636,5568,8080,9281

In totale sono emerse 23 classi per un totale di 16000 punti


Come si vede la distribuzione e' fondamentalmentre asimmetrica con le prima 4 classi che rappresentano la gran parte dei dati. E' stato effettuato un taglio dei dati alle prime 6 classi

 Il file CSV e' stato utilizzato  all'interno dello script seguente in Colab (si tratta di un semplice adattamento del programma di esempio di Tensorflow per la rete  decision tree)


# -*- coding: utf-8 -*-
"""decisiontree_venezia.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1f8HLdjHiDNvpplKzNOVrXuIXrKTay0TS
"""

!pip install tensorflow_decision_forests

import numpy as np
import pandas as pd
import tensorflow_decision_forests as tfdf

path = "/content/classi_nobat_ridotto.csv"
pandas_dataset = pd.read_csv(path)

# Display the first 3 examples.
pandas_dataset.head(3)

np.random.seed(1)
# Use the ~10% of the examples as the testing set
# and the remaining ~90% of the examples as the training set.
test_indices = np.random.rand(len(pandas_dataset)) < 0.1
pandas_train_dataset = pandas_dataset[~test_indices]
pandas_test_dataset = pandas_dataset[test_indices]

print("Training examples: ", len(pandas_train_dataset))

print("Testing examples: ", len(pandas_test_dataset))

tf_train_dataset = tfdf.keras.pd_dataframe_to_tf_dataset(pandas_train_dataset, label='classe')
model = tfdf.keras.CartModel()
model.fit(tf_train_dataset)

tfdf.model_plotter.plot_model_in_colab(model, max_depth=10)

model.compile("accuracy")
print("Train evaluation: ", model.evaluate(tf_train_dataset, return_dict=True))

tf_test_dataset = tfdf.keras.pd_dataframe_to_tf_dataset(pandas_test_dataset, label='classe')
print("Test evaluation: ", model.evaluate(tf_test_dataset, return_dict=True))

from keras.utils import plot_model

plot_model(
    model,
    to_file='model.png',
    show_shapes=False,
    show_dtype=False,
    show_layer_names=True,
    rankdir='TB',
    expand_nested=False,
    dpi=96,
    layer_range=None,
    show_layer_activations=False,
    show_trainable=False
)

!pip install keras-tuner

import keras_tuner as kt

def build_model(hp):
  model = tfdf.keras.CartModel(
      min_examples=hp.Choice("min_examples",
          # Try four possible values for "min_examples" hyperparameter.
          # min_examples=10 would limit the growth of the decision tree,
          # while min_examples=1 would lead to deeper decision trees.
         [1, 2, 5, 10]),
      validation_ratio=hp.Choice("validation_ratio",
         # Three possible values for the "validation_ratio" hyperparameter.
         [0.0, 0.05, 0.10]),
      )
  model.compile("accuracy")
  return model

tuner = kt.RandomSearch(
    build_model,
    objective="val_accuracy",
    max_trials=10,
    directory="/tmp/tuner",
    project_name="tune_cart")

tuner.search(x=tf_train_dataset, validation_data=tf_test_dataset)
best_model = tuner.get_best_models()[0]

print("Best hyperparameters: ", tuner.get_best_hyperparameters()[0].values)
# >> Best hyperparameters:  {'min_examples': 2, 'validation_ratio': 0.0}

model = tfdf.keras.CartModel(min_examples=2, validation_ratio=0.0)
model.fit(tf_train_dataset)

model.compile("accuracy")
print("Test evaluation: ", model.evaluate(tf_test_dataset, return_dict=True))

tfdf.model_plotter.plot_model_in_colab(model, max_depth=10)

Training examples: 13463 Testing examples: 1520


 



al termine si ha una accuracy della porzione di dati di test superiore al 70%

14/14 [==============================] - 1s 8ms/step - loss: 0.0000e+00 - accuracy: 0.7761 Train evaluation: {'loss': 0.0, 'accuracy': 0.7761271595954895} 2/2 [==============================] - 0s 19ms/step - loss: 0.0000e+00 - accuracy: 0.7388 Test evaluation: {'loss': 0.0, 'accuracy': 0.7388157844543457} 



In generale la rete nel caso di incertezza di una classe seleziona sempre classi contingue

 

mercoledì 15 marzo 2023

Local Packages in Golang

 Per poter utilizzare packages locali (librerie di funzioni create dall'utente esterno racchiuse in un package esterno al main) la soluzione e' inserire i le librerie esterne nel subfolder /vendor



nel file main.go l'importazione avviene semplicemente inserendo in import il nome del file della libreria e richiamando le funzioni della libreria come metodi 


import (
    "fmt"
    "scrivi"
)

func main() {
    fmt.Println("luca")
    scrivi.Debug(true)
    scrivi.Log("log")
}


lunedì 13 marzo 2023

Restful server in GO

Un esempio di server Restful con la libreria  go-restful

Nelle note del codice alcune indicazioni su come interagire con il server che si apre su porta 8080


// Pacchetto per utilizzo di Restfull
package main

// inserisci nuovo utente
// curl -X POST -H "Content-Type: application/json" -d '{"Id": "42", "Name": "Luca Innocenti"}' http://localhost:8080/users/
// update utente
// curl -X PUT -H "Content-Type: application/json" -d '{"Id": "42", "Name": "Luca Innocenti"}' http://localhost:8080/users/
// cancella utente con id 42
// ricerca utente con id 42
// curl -X "GET"  http://localhost:8080/users/42

//curl -vX POST http://server/api/v1/places.json -d @testplace.json

import (
    "fmt"
    "log"
    "net/http"

    restful "github.com/emicklei/go-restful/v3"
)

// User is a struct
type User struct {
    Id, Name string
}

// Fprint formats using the default formats for its operands and writes to w.
// Spaces are added between operands when neither is a string.
func New() *restful.WebService {
    service := new(restful.WebService)
    service.
        Path("/users").
        Consumes(restful.MIME_XML, restful.MIME_JSON).
        Produces(restful.MIME_XML, restful.MIME_JSON)

    service.Route(service.GET("/{user-id}").To(FindUser))
    service.Route(service.POST("").To(CreateUser))
    service.Route(service.PUT("/{user-id}").To(UpdateUser))
    service.Route(service.DELETE("/{user-id}").To(RemoveUser))

    return service
}

// GET
func FindUser(request *restful.Request, response *restful.Response) {
    id := request.PathParameter("user-id")
    // here you would fetch user from some persistence system
    usr := User{Id: id, Name: "Luca"}
    response.WriteEntity(usr)
}

// PUT /user-id
func UpdateUser(request *restful.Request, response *restful.Response) {
    parametri := User{Id: request.PathParameter("user-id")}
    //fmt.Println("udpdate user")
    fmt.Println("Numero " + parametri.Id + "Nome " + parametri.Name)
    usr := new(User)
    err := request.ReadEntity(&usr)
    // here you would update the user with some persistence system
    if err == nil {
        response.WriteEntity("modificato")
    } else {
        response.WriteError(http.StatusInternalServerError, err)
    }
}

// POST

func CreateUser(request *restful.Request, response *restful.Response) {
    usr := new(User)
    err := request.ReadEntity(&usr)
    // here you would create the user with some persistence system
    if err == nil {
        response.WriteEntity(usr)
    } else {
        response.WriteError(http.StatusInternalServerError, err)
    }
}

// DELETE /user-id

func RemoveUser(request *restful.Request, response *restful.Response) {
    fmt.Println(request.PathParameter("user-id"))
    log.Printf("DELETE")
    // here you would delete the user from some persistence system
}

func main() {
    restful.Add(New())
    log.Fatal(http.ListenAndServe(":8080", nil))
}






venerdì 11 marzo 2022

Protocol Buffers in Golang

 Protocol Buffers (protobuf) e' una libreria di Google per la serializzazione di dati. Con il termine serializzazione si intende il processo di tradurre una "data structure" (array, struttura, albero binario.....) in un file che conservi oltre che i dati anche la struttura. Una forma tipica di serializzazione sono i file in formato JSON

Per utilizzare protobuf con GoLang si parte dalla definizione della struttura in file .proto. Un semplice modello puo' essere il seguente

syntax = "proto3";

package main;
import "google/protobuf/timestamp.proto";


message Person {
string name = 1;
int32 id = 2;
string email = 3;
google.protobuf.Timestamp last_updated = 5;
}

da notare che il file .proto deve avere lo stesso package del codice che lo utilizzera'

i numeri di fianco ai vari campi sono univoci

il file .proto deve essere compilato tramite il compilatore protoc. Su Debian l'installazione del compilatore avviene mediante

apt install protobuf-compiler
apt install golang-goprotobuf-dev

si crea il file .proto nella stessa directory del sorgente Go e si compila con

protoc --go_out=. *.proto

questo genera un nuovo file con estensione .go. Se si apre il file con editor di testo si osserva che oltre alla struttura dei dati sono state create delle funzioni tipo helper che facilitano l'immissione e gestione della struttura dati

per gestire i dati all'interno del progetto Go, nel caso si usino tipo dati di uso comune tipo datetime gia' formalizzati in protobuf si deve scaricare la loro definizione per esempio

go get github.com/golang/protobuf/ptypes/timestamp

i dati di un protobuf possono essere salvati su un file ma questo sara' binario e non immediatamente human readable

package main

import (
"fmt"
"io/ioutil"
"log"

proto "github.com/golang/protobuf/proto"
)

// apt install protobuf-compiler

// per compilare il file proto
// protoc --go_out=. *.proto

// go get github.com/golang/protobuf/ptypes/timestamp

func main() {
dati_persona := &Person{
Name: "Luca",
Id: 51, //per poco ancora
}

data, err := proto.Marshal(dati_persona)
if err != nil {
log.Fatal("marshaling error: ", err)
}

fmt.Println(data)

out, err := proto.Marshal(dati_persona)
if err != nil {
log.Fatalf("Serialization error: %s", err.Error())
}
if err := ioutil.WriteFile("dati.bin", out, 0644); err != nil {
log.Fatalf("Write File Error: %s ", err.Error())
}
fmt.Println("Write Success")

//Read from file
in, err := ioutil.ReadFile("dati.bin")
if err != nil {
log.Fatalf("Read File Error: %s ", err.Error())
}
dati_persona2 := &Person{}
err2 := proto.Unmarshal(in, dati_persona2)
if err2 != nil {
log.Fatalf("DeSerialization error: %s", err.Error())
}

fmt.Println("Read Success")
fmt.Printf("Nome %s\n", dati_persona2.GetName())
}




venerdì 25 febbraio 2022

Quadtree in Go

metto questo abbozzo di codice perche', nonostante non funzionante, puo' essere interessante

Ai tempi di Fractint su Dos c'e un algoritmo che divideva il piano di quadrati, se i punti estremi del quadrato avevano lo stesso valore allora tutto il quadrato assumeva lo stesso colore dell'insieme di Mandelbrot, in caso contrario il quadrato veniva a sua volta diviso in 4 quadrati e si reiterava

In questo caso ho cercato di fare lo stesso usando le goroutine  ed la ricorsione


package main

import (
    "fmt"
    "math"
    "math/cmplx"
    "reflect"
    "strconv"
    "sync"

    "github.com/fogleman/gg"
)

var wg sync.WaitGroup

// ATTENZION    E il nome delle variabili deve essere in uppercase
// per poter esportare la variabile e
type punto struct {
    Min_x float64
    Max_x float64
    Min_y float64
    Max_y float64
    Itera int64
}

var punti []punto // slice di struct
var transi punto
var mu sync.Mutex

func calcola(a float64, b float64) int {
    var k int
    c := complex(a, b)
    k = 0

    x := complex(0.0, 0.0)
    for k = 0; k < 25; k++ {
        x_a := (x * x)
        x_a = x_a + c
        x = x_a
        if cmplx.Abs(x) > 2 {
            return k
        }
    }
    return k
}

func dividi(min_x float64, max_x float64, min_y float64, max_y float64) {
    defer wg.Done()

    x1 := calcola(min_x, min_y)
    x2 := calcola(max_x, max_y)

    if (x1 != x2) && (max_x-min_x > 0.005) {

        mezzo_x := (max_x - min_x) / 2
        mezzo_y := (max_y - min_y) / 2
        wg.Add(4)
        go dividi(min_x, min_x+mezzo_x, min_y, min_y+mezzo_y)
        go dividi(min_x+mezzo_x, max_x, min_y, min_y+mezzo_y)
        go dividi(min_x, min_x+mezzo_x, min_y+mezzo_y, max_y)
        go dividi(min_x+mezzo_x, max_x, min_y+mezzo_y, max_y)

    } else {
        fmt.Println(strconv.FormatFloat(min_x, 'f', 6, 32) + ";" + strconv.FormatFloat(min_y, 'f', 6, 32) + ";" + strconv.Itoa(x1))

        s := reflect.ValueOf(&transi).Elem()
        s.Field(0).SetFloat(min_x)
        s.Field(1).SetFloat(max_x)
        s.Field(2).SetFloat(min_y)
        s.Field(3).SetFloat(max_y)
        s.Field(4).SetInt(int64(x1))

        mu.Lock()
        punti = append(punti, transi)
        mu.Unlock()
    }

}

func grafica() {
    dc := gg.NewContext(400, 400)

    dx := 3.0 / 400
    dy := 2.0 / 400

    for _, value := range punti {
        fmt.Printf("%d\n", value)
        min_x := math.Round(value.Min_x*dx) + 200
        max_x := math.Round(value.Max_x*dx) + 200
        min_y := math.Round((value.Min_y * dy)) + 200
        max_y := math.Round((value.Max_y * dy)) + 200
        dc.DrawRectangle(min_x, max_x, min_y, max_y)
        dc.SetRGB255(value.Itera, 0, 0)
        dc.Fill()

    }

    dc.SavePNG("mandelbrot.png")
}

func main() {
    wg.Add(1)
    go dividi(-2.0, 1.0, -1.0, 1.0)
    wg.Wait()
    grafica()
}

Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...