ESP01, ATtiny85 e Stepper

Pilotare uno stepper motor con ATtiny, ESP8266 e C#

In questo articolo illustrerò come pilotare un motore passo passo tramite un ATtiny85. L'ATtiny85 viene comandato da un ESP01 che a sua volta riceve dei pachetti UDP da un PC sul quale gira del codice C#.

Architecture

Questo articolo è di fatto il seguito del precedente "Come programmare un ATtiny85".

Il primo passo è programmare l'ATtiny85 per far ruotare lo step motor.

Il secondo è programmare l'ESP01 per ricevere dei comandi da un pc che esegue codice C#.

Il terzo è integrare i tre componenti.

Pilotare uno step motor con un ATtiny85

Per fare un po di pratica con l'ATtiny85 e il motore passo passo ho scritto un "Hello word ATtiny85 & Stepper Motor".

Il primo obbiettivo è di fa girare il motore di 90 gradi in senso orario, attendere 2 secondi e farlo ruotare di 90 gradi in senso antiorario. Dopo 2 secondi il ciclo si ripete.

Il codice è il seguente:

Hello word ATtiny85 & Stepper Motor
#include <Stepper.h>

const float stepsPerRevolution = 2048.0 ;  

const float resolution  = 5.625/64; 
int step_degree(float desired_degree){
    return (stepsPerRevolution/360) * desired_degree;
}

Stepper myStepper(stepsPerRevolution, 1, 3, 2, 0);


void setup() {
  myStepper.setSpeed(7); 
}

void loop() {  
  myStepper.step((step_degree(90)));  
  delay(2000);

  myStepper.step(-(step_degree(90)));
  delay(2000);
}

I collegamenti tra l'ATtiny85 e il motore sono i seguenti:

Wiring

Comunicazione tra ESP01 e Attiny85

Per fare comunicare l'ESp01 e l'ATtiny85 è possibile utilizzare diverse modalità.

Il primo modo, il più semplice, è quello di configurare due piedini dell'ATtiny85 come input. L'ESP01 invierà un segnale logico alto ad uno o l'altro pin in base al comando ricevuto dal codice C#.

Questa modalità potrebbe essere utilizzata solo nel caso in cui abbiamo la necessità di eseguire 2 tipi di rotazioni. Ad esempio "ruota in senso orario di 90 gradi" e "ruota in senso antiorario di 90 gradi". Certo, in base alle varie combianazioni potremmo eseguire più movimenti, ma le combinazioni sono davvero limitate.

La modalità che scelgo invece è configurare i piedini 4 e 5 con una software serial in modo da poter accettare comandi anche complessi. Ad esempio il verso e l'angolo di rotazione.

Poter inviare l'angolo potrebbe essere utile nel caso in cui il circuito si spegne all'improvviso (quando il motore non ha ancora completato la rotazione) e quindi avremo la necessità di doverlo riportare nella posizione iniziale.

Nella soluzione che propondo sotto, ho scelto di inviare stringhe. L' ATtiny85 poi convertirà queste stringhe in float che rappresentano l'angolo e il verso di rotazione.

Il codice da caricare sull'ATtiny85 deve quindi essere modificato per accettare tramite la software serial delle stringhe, convertirle in float e comandare il driver UNL2003 in modo da pilotare il motore passo passo per compiere il numero corretto di step.

Il codice da caricare sull'ATtiny85 è il seguente:

ATtiny85
#include <Stepper.h>
#include <SoftwareSerial.h>


const float stepsPerRevolution = 2048.0 ;  

const float resolution  = 5.625/64; 
int step_degree(float desired_degree){
    return (stepsPerRevolution/360) * desired_degree;
}

Stepper myStepper(stepsPerRevolution, 1, 3, 2, 0);


#define rxPin 4
#define txPin 5
SoftwareSerial mySerial(rxPin, txPin);

boolean isBysy;

void setup() {
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);

  mySerial.begin(9600);

  while (!mySerial) {
    ; // wait for serial port to connect. Needed for Native USB only
  }   

  myStepper.setSpeed(7); 

  //ruota 45 gradi e torna indietro per segnalare l'accensione
  myStepper.step((step_degree(45)));  
  delay(500);

  myStepper.step(-(step_degree(45)));
  delay(100);
  isBysy=false;
}


void loop() {
  int recvN;
  while(1){   
    String recvString;
    while(mySerial.available()){
      i f(isBysy==false){
        recvString=mySerial.readString();
      
        isBysy = true;       
        float n = recvString.toFloat();
        myStepper.step((step_degree(n))); 
        isBysy = false;
        
     }
    }      
  }
}

A questo punto è ora di preparare l' ESP01 che dovrà compiere le seguenti azioni:

  1. Connettersi alla rete
  2. Attendere l'arrivo di un pacchetto UDP
  3. Inviare un pacchetto UDP come "acknowledgement"

Il codice da caricare sull'ESp01 è il seguente:

ESP8266
#include <ESP8266WiFi.h>
#include <WiFiUDP.h>

const char* ssid = "BFF-IOT"; //case sensitive
const char* password = "<PASSWORD RETE>";

WiFiUDP UDP;


void setup() {
  Serial.begin(9600);
  Serial.println("Connecting");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    //Serial.print(".");
  }
  
  //Serial.println("Connected, IP address: ");
  //Serial.println(WiFi.localIP());

  i f(UDP.begin(4210) == 1)
  {
    //Serial.println("Connected");
  }
  else{
    //Serial.println("Connection failed");
  }
}
int valore;

char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; 


void loop() {

  int packetSize = UDP.parsePacket();
  i f(packetSize)
  {
    int len = UDP.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);
    i f (len > 0)
    {
      packetBuffer[len] = '\0';
    }
 
    UDP.beginPacket("192.168.100.239", 82); //ip del server
    UDP.write("Ricevuto:");
    UDP.write(packetBuffer);
    UDP.endPacket();

    String n = String(packetBuffer);
    sendInt(n);
  }
}


void sendInt(String s){
  Serial.print(s);  
  Serial.flush();
}

Il codice C# per l'invio e la ricezione dei pachetti UDP è davvero banale:

MainWindow.xaml.cs
public partial class MainWindow : Window
    {
        //string Esp01Ip = "192.168.222.26";
        string Esp01Ip = "192.168.99.140";
        int esp01Port = 4210;

        const int listeningPort = 82;

        UdpClient receivingUdpClient = new UdpClient(listeningPort);
        IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);


        public MainWindow()
        {
            InitializeComponent();

            Task.Run(() =>
            {
                Receive();
            });

        }


        private void BtnShow_Click(object sender, RoutedEventArgs e)
        {
            Send(Esp01Ip, esp01Port, "90");
        }

        private void BtnHide_Click(object sender, RoutedEventArgs e)
        {
            Send(Esp01Ip, esp01Port, "-90");
        }

        private void BtnRotate_Click(object sender, RoutedEventArgs e)
        {
            Send(Esp01Ip, esp01Port, TxtDegree.Text);
        }

        private void Send(string remoteIp, int remotePort, string message)
        {

            UdpClient udpClient = new UdpClient(remoteIp, remotePort);
            Byte[] sendBytes = Encoding.ASCII.GetBytes(message);
            udpClient.Send(sendBytes, sendBytes.Length);
        }


        private void Receive()
        {
            while (true)
            {
                Byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
                {
                    TxtReceived.Text += System.Text.ASCIIEncoding.ASCII.GetString(receiveBytes) + Environment.NewLine;
                }));
            }
        }
    }
MainWindow.xaml
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
        <ColumnDefinition></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="1" Grid.Column="1">
        <Button  x:Name="BtnShow" Content="Show" Click="BtnShow_Click"  ></Button>
        <Button  x:Name="BtnHide" Content="Hide" Click="BtnHide_Click"></Button>
        <TextBox x:Name="TxtDegree" Text="360" Margin="0,10,0,3" ></TextBox>
        <Button  x:Name="BtnRotate" Content="Rotate" Click="BtnRotate_Click"></Button>

    </StackPanel>

    <TextBlock Grid.Row="0" Grid.RowSpan="3" Grid.Column="2" x:Name="TxtReceived"></TextBlock>
</Grid>

La breadboard con tutti i componenti è:

Breadboard

Buon divertimento!

Se hai voglia di lasciarmi un commento mi trovi su Linkedin.