home
erste Version am 27.11.2016
letzte Änderung am 27.11.2016

ESP8266 und 3.3V-Regler via MOSFET schalten

In meinem geplanten Temperatur- und Helligkeits-Logger soll wieder ein ESP8266 die tägliche WLAN-Kommunikation übernehmen.
Diesmal möchte ich das Teil jedoch nicht über ein Relais ein/ausschalten.
Stattdessen habe ich mir zehn IRFD 9024 P-Kanal Leistungs-MOSFET bei Reichelt bestellt und gestern ein entsprechendes Experimentier-Board aufgebaut.

Ziel soll auch hier wieder sein, einen Akku-Pack als Stromversorgung für die Gesamt-Schaltung einzusetzen und den ESP8266 zwecks Akku-Laufzeit-Verbesserung nur bei Bedarf (für wenige Sekunden am Tag) zuzuschalten.



Der Schaltplan sieht folgendermaßen aus:
Schaltplan

Der 5V Ausgang des Arduino ist nicht geeignet, die Schaltung zu speisen. Damit kommt zuverlässig Müll !!!

Und noch ein Foto vom Experimentier-Board:
Foto Leiterplatte


Den Programmcode (weitestgehend übernommen aus ESP8266-AnsteuerungV7.ino) habe ich diesmal auf drei Dateien verteilt. Als erster Test wird einfach mit dem Script SocketServerESP8266v7.py kommuniziert, welches unter der IP-Adresse 192.168.42.80 auf dem Port 2626 lauscht und nach Empfang von "GET_TIME" mit dem aktuellen Timestamp antwortet.

MOSFET_test1.ino
#include "ESP8266_PowerSave.h"

#define RX_PIN_TO_ESP8266        2 // mit ESP8266-TX verbinden
#define TX_PIN_TO_ESP8266        3 // mit ESP8266-RX verbinden
#define ESP8266_CHIP_ENABLE_PIN  6 // Chip-Enable des ESP8266
#define ESP8266_POWER_PIN        7 // Stromversorgung für den 3.3V-Regler


ESP8266_PowerSave ESP8266(RX_PIN_TO_ESP8266, TX_PIN_TO_ESP8266);

char buffer_g[110];

void setup() {
  Serial.begin(115200);

  ESP8266.setup(ESP8266_POWER_PIN, ESP8266_CHIP_ENABLE_PIN, 38400, buffer_g, 110);
  ESP8266.enable();
  Serial.println("ESP8266 eingeschaltet");
}

void loop() {
  char stat;

  if(ESP8266.wait_until_responsive(10)) {
    Serial.print("ESP8266 Status:  ");
    while((stat=ESP8266.get_TCPIP_status())!=2)  {
      Serial.print(stat, DEC);  Serial.print(" ");
      delay(100);
    }
    Serial.println(stat, DEC);
 
    if(ESP8266.connect_TCPIP(10)) {
      Serial.println("via TCPIP verbunden");
      Serial.println("sende:  GET_TIME");
      if(ESP8266.TCPIP_send((char*)"GET_TIME")) {
        Serial.print("empfangen:  ");
        Serial.println(buffer_g);
      } else {
        Serial.println("Fehler beim Empfang!");
      }
    }
  } else {
    Serial.println("ESP8266 antwortet nicht!");
  }

  ESP8266.disable();
  while(1);
}


ESP8266_PowerSave.h
#ifndef ESP8266_PowerSave_h
#define ESP8266_PowerSave_h

#include <SoftwareSerial.h>
#include <Arduino.h>

class ESP8266_PowerSave {
  public:
            ESP8266_PowerSave(uint8_t rx, uint8_t tx) : esp(rx, tx) { rx_pin=rx; tx_pin=tx; };
    void    setup(uint8_t power, uint8_t enable, long spd, char *buf, uint8_t blen);
    void    enable(void);
    void    disable(void);
    void    init(void);

    char    wait_until_responsive(uint8_t timeout_seconds);
    void    emptyBuffer(void);
    uint8_t get_TCPIP_status(void);
    uint8_t TCPIP_send(char *data);
    char    *getChecksum(char *strg);
    uint8_t connect_TCPIP(uint8_t connect_retries);
    uint8_t is_connected_via_TCPIP(void);

  private:
    uint8_t  power_pin;
    uint8_t  enable_pin;
    uint8_t  rx_pin;
    uint8_t  tx_pin;
    long     speed;
    uint8_t  enabled;
    char     *buf_ptr;
    uint8_t  buf_len;

    SoftwareSerial  esp;
};
#endif



ESP8266_PowerSave.cpp
#include "ESP8266_PowerSave.h"

/* -------------------------------------------------------------------
 * Setup des ATmega zur Kommunikation mit dem ESP8266.
 */
void ESP8266_PowerSave::setup(uint8_t power, uint8_t enable, long spd, char *buf, uint8_t blen) {
  enabled=0;
  power_pin=power;
  enable_pin=enable;
  speed=spd;
  buf_ptr=buf;
  buf_len=blen;

  pinMode(power_pin, OUTPUT);
  pinMode(enable_pin, OUTPUT);
  digitalWrite(power_pin, HIGH);  // ESP ausschalten
  delay(200);
}

/* -------------------------------------------------------------------
 * ESP8266 power on und enable
 */
void ESP8266_PowerSave::enable(void) {
  if(!enabled) {
    digitalWrite(power_pin, LOW);     // ESP8266 mit Strom versorgen
    delay(10);
    digitalWrite(enable_pin, HIGH);   // ESP8266 aktivieren
    esp.begin(speed);
    enabled=1;
  }
}

/* -------------------------------------------------------------------
 * ESP8266 disable und power off
 */
void ESP8266_PowerSave::disable(void) {
  digitalWrite(tx_pin, LOW);      // keine Spannung am abgeschalteten ESP8266
  digitalWrite(enable_pin, LOW);  // ESP8266 deaktivieren
  delay(10);
  digitalWrite(power_pin, HIGH);  // Strom für ESP8266 abschalten
  enabled=0;
}

/* -------------------------------------------------------------------
 * Stellt den ESP8266 auf
 *    "station mode",
 *    "single connection" und
 *    connect to AP "FreifunkWees01.2 (http://ffw)"
 * ein.
 */
void ESP8266_PowerSave::init(void) {
  esp.println(F("AT+CWMODE_CUR=1"));
  esp.println(F("AT+CIPMUX=0"));
  esp.println(F("AT+CWAUTOCONN=1"));
  esp.println(F("AT+CWJAP_CUR=\"FreifunkWees01.2 (http://ffw)\",\"\""));
}

/* -------------------------------------------------------------------
 * Wartet (bis zu "timeout_seconds" Sekunden) darauf, dass der ESP8266
 * mit "OK" auf einen "AT"-Befehl reagiert.
 * Liefert 1, wenn der ESP8266 korrekt reagiert hat. Sonst 0.
 */
char ESP8266_PowerSave::wait_until_responsive(uint8_t timeout_seconds)  {
  for(uint8_t i=0; i<timeout_seconds; i++)  {
    esp.println(F("AT"));
    if(esp.find((char*)"\r\nOK\r\n"))  { // wartet den Default-Timeout von einer Sekunde
      emptyBuffer();
      return(1);
    }
  }
  return(0);
}

/* -------------------------------------------------------------------
 * Receive-Buffer leerlesen
 */
void ESP8266_PowerSave::emptyBuffer(void)  {
  char c;
  unsigned long timeout;

  if(esp.available()==0)  {
    return;
  }
  timeout=millis()+100;  // maximal 1/10 Sekunde lang leerlesen
  while(esp.available() && millis()<timeout)  {
    c=esp.read();
  }
}

/* -------------------------------------------------------------------
 * Liefert den Status des ESP8266 als Zahl.
 *  0 : Fehler
 *  2 : Got IP
 *  3 : Connected     (via  TCP/IP)
 *  4 : Disconnected  (from TCP/IP)
 */
uint8_t ESP8266_PowerSave::get_TCPIP_status(void) {
  uint8_t state=0;
  esp.println(F("AT+CIPSTATUS"));
  if(esp.find((char*)"\r\nSTATUS:")) {
    state=esp.parseInt();
  }
  esp.find((char*)"\r\nOK\r\n"); // Rest weglesen
  return(state);
}

/* -------------------------------------------------------------------
 * Daten ("data") via IP senden und Antwort (in buf_ptr) empfangen.
 * Für die Gut-Meldung der Funktion muss auch die empfangene Checksum
 * zu den empfangenen Daten passen.
 * Liefert 1, wenn alles geklappt hat. Sonst 0.
 */
uint8_t ESP8266_PowerSave::TCPIP_send(char *data) {
  int len;
  char *ptr, *ptr_cs, rc=0;
  unsigned long timeout;

  esp.print(F("AT+CIPSEND="));
  esp.println(strlen(data)+5);  // +5 -> ',' + 4 Byte Checksum
  delay(20);  // etwas warten zwischen Kommando und Daten - sonst klappts nicht
  esp.print(data);
  esp.print(",");
  esp.println(getChecksum(data));

  // Antwort: "+IPD,nn:<nn Byte Daten>" (bei CIPMUX=0 = "single connection")
  if(!esp.find((char*)"+IPD,")) { // wartet gemäß Stream.timeout (Default=1Sek) auf Daten
    return(0);
  }
  len=esp.parseInt();
  if(len>=buf_len) {
    return(0);  // da kommen mehr Daten, als in buf_ptr passen
  }
  esp.find(':');
  esp.readBytes(buf_ptr, len);
  *(buf_ptr+len)='\0';       // String-Ende setzen
  *(buf_ptr+len-5)='\0';     // Checksum abtrennen
  ptr_cs=buf_ptr+len-4;      // Pointer auf den Checksum-String
  ptr=getChecksum(buf_ptr);  // Checksum-String für Daten (ohne Checksum) bilden
  if(strcmp(ptr, ptr_cs)==0)  {   // beide Checksummen vergleichen
    rc=1;
  } else {
    rc=0;
  }
  delay(100);   // mit etwas Wartezeit zwischen TCPIP_send()'s ist es sicherer
  emptyBuffer();
  return(rc);
}

/* -------------------------------------------------------------------
 * Liefert eine [primitive] Prüfsumme für den Null-terminierten
 * String "strg" als Pointer auf einen String (4 Byte + '\0').
 */
char *ESP8266_PowerSave::getChecksum(char *strg) {
  static char buf[5];
  int cs=0;
  for(char *ptr=strg; *ptr!='\0'; ptr++)  {
    cs+=*ptr;
  }
  snprintf(buf, 5, "%04x", cs);
  return(buf);
}

/* -------------------------------------------------------------------
 * "connect_retries" mal versuchen, die Verbindung zum
 * Python-Script herstellen.
 * Liefert 1, wenn verbunden. Sonst 0.
 */
uint8_t ESP8266_PowerSave::connect_TCPIP(uint8_t connect_retries)  {
  for(uint8_t i=0; i<connect_retries; i++) {
    esp.println(F("AT+CIPSTART=\"TCP\",\"192.168.42.80\",2626"));
    if(esp.find((char*)"\r\nOK\r\n"))  {
      delay(50);  // den braucht er....
      if(is_connected_via_TCPIP()) {
        return(1);
      }
    }
  }
  return(0);
}

/* -------------------------------------------------------------------
 * Verbindungs-Status abfragen (3 = connected via TCP/IP).
 * Liefert 1, wenn der ESP8266 via TCP/IP verbunden ist. Sonst 0.
 */
uint8_t ESP8266_PowerSave::is_connected_via_TCPIP(void) {
  return(get_TCPIP_status()==3);
}



Die entsprechende Debug-Ausgabe sieht so aus:
ESP8266 eingeschaltet
ESP8266 Status:  5 5 5 5 5 5 5 5 5 5 0 2
via TCPIP verbunden
sende:  GET_TIME
empfangen:  2016.11.27 12:24:43