ESP8266 lettura calibro

Ciao a tutti,
spero di aver aperto una discussione nella zona giusta.

Sto ultimamente lavorando ad un progetto dove utilizzare una scheda ESP8266 per leggere valori dal calibro.
Ero riuscito in passato a costruire un progetto con Arduino UNO.
Ora per necessità devo utilizzare questa scheda che conosco.
Qualcuno riesce a darmi un feedback?

 #include <EEPROM.h>
#include <driver/adc.h>

#define ESPDRO_VERSION "0.99.1"

int dataPin = 36; // yellow
int clockPin = 39; // green

#define ADC_TRESHOLD 800
#define DEBUG_SIGNAL 0
#define BIT_READ_TIMEOUT 100
#define PACKET_READ_TIMEOUT 250
#define PACKET_BITS 24
#define MIN_RANGE -(1<<20)
#define DRO_BUFFER_SIZE  0x1000

struct Reading
{
    uint32_t timestamp;
    int32_t microns;

    Reading() : timestamp(millis()), microns(MIN_RANGE) {}

    Reading &operator=(const Reading &obj)
    {
        timestamp = obj.timestamp;
        microns = obj.microns;
        return *this;
    }
};

Reading dro_buffer[DRO_BUFFER_SIZE] = {};
size_t dro_index = 0;

unsigned char eepromSignature = 0x5A;

bool client_mode = 1;
bool stream_mode = 1;
volatile bool debug_mode = 0;

void log(char *fmt, ... )
{  
  char buf[128];
  va_list args;
  va_start (args, fmt);
  vsnprintf(buf, 128, fmt, args);
  va_end (args);
  Serial.println(buf);
}

int getBit() {
    int data;

    if (debug_mode) {
      // debug code to sample the data reads
      log("CLK:%6d DATA:%6d\n", analogRead(clockPin), analogRead(dataPin));
    }
        
    int readTimeout = millis() + BIT_READ_TIMEOUT;
    while (analogRead(clockPin) > ADC_TRESHOLD) {
      if (millis() > readTimeout)
        return -1;
    }
    
    while (analogRead(clockPin) < ADC_TRESHOLD) {
      if (millis() > readTimeout)
        return -1;
    }
    
    data = (analogRead(dataPin) > ADC_TRESHOLD)?1:0;
    return data;
}

long getPacket() 
{
    long packet = 0;
    int readTimeout = millis() + PACKET_READ_TIMEOUT;

    int bitIndex = 0;
    while (bitIndex < PACKET_BITS) {
      int bit = getBit();
      if (bit < 0 ) {
        // bit read timeout: reset packet or bail out
        if (millis() > readTimeout) {
          // packet timeout
          return -1;
        }
        
        bitIndex = 0;
        packet = 0;
        continue;
      }

      packet |= (bit & 1)<<bitIndex;
      
      bitIndex++;
    }
    
    return packet;
}

long getMicrons(long packet)
{
  if (packet < 0)
    return MIN_RANGE;
    
  long data = (packet & 0xFFFFF)*( (packet & 0x100000)?-1:1);

  if (packet & 0x800000) {
        // inch value (this comes sub-sampled) 
        data = data*254/200;
  }

  return data;
}

void spcTask( void * parameter )
{
    uint32_t lastReadTime = millis();

    for(;;) { 
      long packet = getPacket();
      
      if (packet < 0) {
        // read timeout, display?
        if (millis() > lastReadTime + PACKET_READ_TIMEOUT) {
          // advance last read to time-out
          lastReadTime = millis();
          log("* %d: no SPC data", lastReadTime);
        }
      } else {

        // add to local queue
        //log("* %d: microns=%d raw=0x%08X", millis(), getMicrons(packet), packet);
        size_t new_dro_index = (dro_index+1) % DRO_BUFFER_SIZE;
        dro_buffer[new_dro_index].timestamp = millis();
        dro_buffer[new_dro_index].microns = getMicrons(packet);
        dro_index = new_dro_index;

        // broadcast to all websocket clients
        char buf[128];
        sprintf(buf, "{\"axis0\":%d,\"ts\":%d}", dro_buffer[new_dro_index].microns, dro_buffer[new_dro_index].timestamp);
        //webSocket.broadcastTXT(buf);  // Rimossa, poiché non ci sono più WebSocket
      }
    } 
    //vTaskDelete( NULL ); 
}

void setup()
{
    pinMode(dataPin, INPUT);
    pinMode(clockPin, INPUT);

    EEPROM.begin(512);
    delay(20);

    Reading start_reading;
    dro_buffer[dro_index] = start_reading;

    Serial.begin(115200);
    log("EspDRO %s initialized.", ESPDRO_VERSION);

    analogReadResolution(11);

    analogSetAttenuation(ADC_6db);
    adc1_config_width(ADC_WIDTH_BIT_10);

    // Rimuovi ssid e password, in quanto non sono più utilizzati
    // Rimuovi anche le chiamate a server.begin() e webSocket.begin(), in quanto non ci sono più server web e WebSocket

    // Altre inizializzazioni necessarie

    // ... (rimuovi le parti del codice relative al server web e alle WebSocket)
}

void loop()
{
  // Rimuovi la chiamata a server.handleClient() e webSocket.loop(), poiché non ci sono più server web e WebSocket

  if(Serial.available() > 0)
  {
      String serial_cmd = Serial.readStringUntil('\n');
      log("Received: cmd='%s'", serial_cmd.c_str());

      if (serial_cmd.startsWith("stop")) {
        // serial_cmd.substring(14).toInt();
        stream_mode = 0;
      } else if (serial_cmd.startsWith("start")) {
        stream_mode = 1;
      } else if (serial_cmd.startsWith("debug")) {
        debug_mode = !debug_mode;
      }
  }
}

Vuoi dei commenti sul codice che hai scritto?...

Ammetto che anche io come @Datman non ho ben capito quale sia la richiesta...

Il progetto in questione inoltre mi sembra ben documentato.
Quali sono i tuoi dubbi?

Edit...
Forse ho capito, il progetto nasce per ESP32 e tu lo vuoi adattare all'ESP8266?
Non ne vedo molto il senso se devo essere sincero.

Ciao esatto.
Il senso è che ne ho tante di esp8266. se potevo volevo provare a sfruttarle.
Se non si può è uguale, la comprerò

Tutto è possibile, ma questo progetto fa uso di hardware specifico della ESP32 (il modulo ADC) che è diverso da quello usato su ESP8266.

Inoltre viene usato anche un task FreeRTOS che non è disponibile sulla piattaforma Arduino per ESP8266.

In altre parole, fai prima a riscriverlo da zero.
Tra l'altro il protocollo Digimatic di Mitutoyo è ormai di pubblico dominio, si trova molto materiale online al riguardo.

Di questo progetto è sicuramente interessante il fatto che non è necessario hardware aggiuntivo per "adattare" i livelli TTL del segnali di uscita, ma i traslatori di livello ormai si trovano anche nelle patatine :joy:

Purtroppo dell'ultima parte non ho capito molto...
Parli dei regolatori di tensione? Io feci un progetto per Arduino con un level shifter per regolare la tensione per alimentarlo. Visto che sei molto ferrato riusciresti a darmi una mano nello scrivere questo codice da zero? GRAZIE

Se leggi la documentazione del progetto, puoi vedere che il segnale di uscita del calibro ha un livello di tensione di circa 1.5V mentre l'ESP32 è alimentato a 3.3V

Quest significa che il segnale si pone proprio a metà tra il livello massimo per essere considerato LOW (0.8V) e quello minimo per essere considerato HIGH (2V).

Io avrei usato un banale transitor, ma l'autore del progetto ha deciso di usare il modulo ADC.

Che poi magari il calibro che hai tu, funziona con un livello di tensione diverso ed il problema non si pone nemmeno...

1 Like

Il progetto di qualche tempo qua è questo:

Il level shifter che ho è questo che si vede nelle foto.
Io vorrei "trasformare" questo progetto per esp8266.
Per due motivi : minor compattezza del progetto
E in futuro... per adesso non importa... utilizzare il segnale wi-fi per vedere i dati.
Quindi non mi servirebbe ne anche LCD nel progetto nuovo

Sketch funzionante con ESP8266.
Non ho approfondito il discorso dei pollici, ma se ti serve verifico.

#include <Arduino.h>       // Per le definizioni dei pin D1, D2, Dx

const uint8_t CLK_PIN = D1;
const uint8_t DAT_PIN = D2;

const uint32_t maxFrameTime = 20000;           // 20ms
volatile uint32_t startFrame;                  // Check max frame time
volatile uint32_t rowValue = 0x00FFFFFF;       // 24 bit data lenght
volatile uint8_t bitPos = 0;

double measure = 0.0;

void IRAM_ATTR isr() {
  if (!bitPos) {
    startFrame = micros();
  }
  if (bitPos < 24 ) {
    digitalRead(DAT_PIN) ? bitSet(rowValue, bitPos) : bitClear(rowValue, bitPos);
  }
  bitPos++;
}

void setup() {
  Serial.begin(115200);
  Serial.println("START");
  pinMode(CLK_PIN, INPUT);
  pinMode(DAT_PIN, INPUT);
  attachInterrupt(digitalPinToInterrupt(CLK_PIN), isr, RISING);
}

void loop() {

  if ((micros() - startFrame > maxFrameTime && bitPos != 0)){
    // Qualcosa è andato storto o il micro era impegnato in altro.
    // Ripristino variabili per la lettura successiva
    rowValue = 0x00FFFFFF;
    bitPos = 0;
  }

  if (bitPos == 24) {
    double sign = bitRead(rowValue, 20) ? -1.0 : 1.0;
    measure = ((rowValue & 0b000011111111111111111111)) * sign;
    Serial.print((measure / 100.0), 2);
    Serial.println(" mm");
    rowValue = 0x00FFFFFF;
    bitPos = 0;
  }

}
1 Like

Ciao, riguardo al circuito che hai postato qua sopra, potresti spiegarmi in che modo viene stabilito il valore delle resistenze poste tra le uscite del calibro e la base dei transistor?
Grazie!

Il circuito l'ho preso a caso da internet giusto come esempio, non mi sono soffermato sui valori a dire il vero.

Comunque essenzialmente si tratta di mandare in saturazione il transistor. Siccome si tratta di piccoli segnali la corrente Ic sarà bassissima, quindi basta una corrente sull'ordine dei 100uA di Ib per mandare in saturazione il transistor.
Con una resistenza di 15K e il valore di tensione in ingresso di 1.5V se non ho fatto male i conti equivale ad una corrente Ib di 60uA circa (assumendo una Vbe di 0.6v), probabilmente è meglio abbassarla a 10K.

Se vuoi realizzare questo progetto però lascia perdere lo sketch cervellotico del link proposto: è inutilmente complesso e limitato esclusivamente all'ESP32.

Lo sketch proposto alla fine del post invece l'ho sviluppato e testato su ESP8266, ma basta aggiornare i pin e la funzione ISR per adattarlo a qualsiasi microcontrollore.

E' stato sviluppato usando un comune level converter basato sul chip TXS0108E che non inverte lo stato logico dei segnali come invece fa lo schema basato sui transistor.
Pertanto se vorrai usare lo schema proposto nell'immagine, dovrai abilitare la resistenza di pullup sui pin di ingresso ed invertire di conseguenza il bitSet ed il bitClear nella ISR.

Ciao, grazie per la risposta rapida!
Sono capitato qua perché sono nella stessa situazione del creatore del topic: devo leggere i dati di un comparatore digitale, ho trovato quel progetto su github ma ho a disposizione solo qualche esp8266 (nodemcu).
Il caso vuole che in un kit di componenti vari acquistato anni fa erano inclusi un po' di 2n2222a, per cui pensavo di optare per quella soluzione.
Vedo se trovo un paio di resistenze da 10k e stasera provo a mettere insieme il tutto, ti aggiornerò sui risultati.
Grazie!

Ok, ho lo stesso problema con il tuo codice e con quello di github (riadattato da me eliminando tutta la parte legata al web e all'ADC).
Se il comparatore mostra 0 a schermo allora sul monitor seriale vedo 0mm (corretto).
Se il comparatore mostra un qualunque numero diverso da 0 non ottengo nessun valore.
Provando a mettere stampe qua e là ho notato che la lettura finisce sempre nella condizione di "timeout" presente nella funzione loop, anche se estendo il tempo di timeout di 10 volte.
Ora resta da capire se ho sbagliato qualcosa io o se questo comparatore parla un'altra lingua, considerando che per qualche motivo ha una porta micro-usb invece di quella del progetto su github.
edit: Aggiungo che sostituendo "INPUT_PULLUP" con "INPUT" non cambia nulla, ma anche con un semplice programma di prova vedo che i la funzione digitalread restituisce 1 sugli ingressi non collegati anche senza esplicitare "INPUT_PULLUP", sembrano già avere il pullup collegato di default. O forse leggere un input flottante non ha molto senso
edit2: Problema risolto, non so come.
Lo stesso codice ora sembra funzionare. Nel frattempo, tra tutte le prove fatte per risolvere, ho anche aggiornato le librerie delle schede dell'ide arduino. Che ci fosse qualche problema lì? Mah
In ogni caso ti ringrazio per il prezioso aiuto. Ho soltanto dovuto dividere per 1000.0 perchè il mio comparatore ha una risoluzione di 0.001mm

#include <ESP8266WiFi.h>
#include <WiFiClient.h>

// Pin Configuration - !!! Update This !!! 
int dataPin = 2;
int clockPin = 0;

const uint32_t maxFrameTime = 20000;           // 20000 20ms
volatile uint32_t startFrame;                  // Check max frame time
volatile uint32_t rowValue = 0x00FFFFFF;       // 24 bit data lenght
volatile uint8_t bitPos = 0;

double measure = 0.0;


void IRAM_ATTR isr() {
  if (!bitPos) {
    startFrame = micros();
  }
  if (bitPos < 24 ) {
    digitalRead(dataPin) ? bitClear(rowValue, bitPos) : bitSet(rowValue, bitPos);
  }
  bitPos++;
}


void setup()
{
  delay(1000);
  Serial.begin(115200);
  delay(1000);
  
  pinMode(dataPin, INPUT_PULLUP);     
  pinMode(clockPin, INPUT_PULLUP);
/*
  // start WIFI connection
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
*/
  attachInterrupt(digitalPinToInterrupt(clockPin), isr, FALLING);
  Serial.println("START");
}


void loop()
{
  if ((micros() - startFrame > maxFrameTime && bitPos != 0)){
    Serial.println(bitPos);
    // Qualcosa è andato storto o il micro era impegnato in altro.
    // Ripristino variabili per la lettura successiva
    rowValue = 0x00FFFFFF;
    bitPos = 0;
  }

  if (bitPos == 24) {
    double sign = bitRead(rowValue, 20) ? -1.0 : 1.0;
    measure = ((rowValue & 0b000011111111111111111111)) * sign;
    Serial.print((measure / 100.0), 2);
    Serial.println(" mm");
    rowValue = 0x00FFFFFF;
    bitPos = 0;
  }


 
}```
1 Like

Purtroppo questi strumenti di produzione cinese non hanno uno standard ben definito (come ad esempio accade con i prodotti Mitutoyo) e quindi tra un produttore e l'altro ci possono essere leggere differenze nel timing dei segnali.

Sul micro tu dovresti avere dei segnali tipo questi
image

Mentre nel mio caso, usando un level converter, sono invertiti e quindi quello che per te è il fronte di discesa sul quale vai ad agganciare la ISR per me diventa il fronte di salita.

Purtroppo non ho a disposizione un oscilloscopio (per ora). Però come ho scritto nell'ultimo dei 200 edit all'ultimo post (XD) alla fine il tutto ha iniziato magicamente a funzionare, non so se per via del fatto che ho aggiornato la libreria delle schede esp nell'ide arduino o chissà che altro.

Ciao, scusa se te l'ho chiedo ma che progetto stai realizzando? magari stiamo facendo la stessa cosa...

Nulla di particolarmente complesso. Voglio analizzare l'allungamento dell'asse di un macchinario in funzione della temperatura, per poterlo compensare. Per cui con un ESP registro la temperatura mediante un termistore, e con un altro ESP registro la dilatazione loggando le letture del comparatore digitale. Salvo i risultati su ThingSpeak e poi li analizzo in un secondo momento.

Per eseguire questi task ne basta uno di ESP.
Se poi parliamo di ESP32 è pure troppo :sweat_smile:

Devo rilevare la temperatura dietro al macchinario e misurare la lunghezza davanti. Avendo a disposizione 4 o 5 ESP tanto vale usarne 2

Tutto sto lavoro e scopro che il comparatore si spegne da solo rendendo impossibile fare ciò che devo fare :smiling_face_with_tear:
Ho provato a contattare il produttore ma non sono sicuro che sia possibile disattivare l'auto-off.
In officina abbiamo anche uno di questi comparatori: link , la specifica del protocollo mi sembra simile a quella di cui abbiamo parlato no? C'è solo da inviare un impulso per avviare la trasmissione o sbaglio? Però tocca creare un connettore in qualche modo.
Dimmi se sbaglio, ma dovrei poter collegare Data e CLK direttamente all'esp attivando il pullup interno, mentre il segnale REQ dovrei pilotarlo con il 2N2222A che mette a massa l'ingresso del calibro?
Resta da capire se pure questo si spegne dopo 5 minuti...