How a Web Server on an STM32F103C8T6 can be used to control a Relay Switch

Here is the circuit showing how a Web Server on an STM32F103C8T6 can be used to control a Relay Switch:

BluePill As A Web Server With A Relay SwitchThe Fritzing code used to create that diagram/image can be downloaded here.

An ENC28J60 ethernet module is used to connect the STM32F103C8T6 (BluePill) to a network.

Here is the source code for the sketch to load into the STM32F103C8T6 (BluePill):

/*
  Web Server
 
 A simple web server that shows the value of the analog input pins.
 blinks the built in LED, or toggles a Relay Switch (check the comments)

 From: earl@microcontrollerelectronics.com
 Also Demonstrates checking the ENC28J60 and keeping it from freezing.

ENC28J60  STM32F103
SCK        PA5
SO         PA6
SI         PA7
CS         PA4
RST        PA0

VCC - 5V
GND - GND

--ENC28J60--
CLK  INT
WOL  SO
SI   SCK
CS   RESET
VCC  GND

             +-----------------[USB]-----------------+
  [SS2|PB12] | [31]                            [Gnd] |
 [SCK2|PB13] | [30]                  +---+     [Gnd] |
[MISO2|PB14] | [29]    +-----+       |0 0|     [3V3] |
[MOSI2|PB15] | [28]    |Reset|       |x x|   [Reset] | 
       [PA8] | [27]    +-----+       |1 1|      [ 0] | [PB11|SDA2|RX3] 
   [TX1|PA9] | [26]                  +---+      [ 1] | [PB10|SCL2|TX3]
  [RX1|PA10] | [25]                   ^ ^       [33] | [PB1]
 [USB-|PA11] | [24]            Boot1--+ |       [ 3] | [PB0|A0]
 [USB+|PA12] | [23]            Boot0----+       [ 4] | [PA7|A1|MOSI1]
      [PA15] | [20]                             [ 5] | [PA6|A2|MISO1]
       [PB3] | [19]        +-----------+        [ 6] | [PA5|A3|SCK1]
       [PB4] | [18]        | STM32F103 |        [ 7] | [PA4|A4|SS1]
       [PB5] | [17]        | Blue Pill |        [ 8] | [PA3|A5|RX2]
  [SCL1|PB6] | [16]        +-----------+        [ 9] | [PA2|A6|TX2]
  [SDA1|PB7] | [15]                             [10] | [PA1|A7]
       [PB8] | [32]                             [11] | [PA0|A8]
       [PB9] | [PB9]                            [12] | [PC15]
             | [5V]      +---------------+      [13] | [PC14]
             | [Gnd]     |    ST-Link    |      [14] | [PC13|LED]
             | [3V3]     |3V3 DIO CLK GND|     [Vbat]| 
             +-------------+---+---+---+-------------+
                           |   |   |   |
*/

#define BOARD_SPI1_NSS_PIN        PA4
#define BOARD_SPI1_SCK_PIN        PA5
#define BOARD_SPI1_MISO_PIN       PA6
#define BOARD_SPI1_MOSI_PIN       PA7

#include <libmaple/iwdg.h>
#include <SPI.h>
#include <UIPEthernet.h>

// ..utility\Enc28J60Network.h file - 
//move readReg() subroutine def from private to public 

#define NET_ENC28J60_EIR          0x1C
#define NET_ENC28J60_ESTAT        0x1D
#define NET_ENC28J60_ECON1        0x1F
#define NET_ENC28J60_EIR_RXERIF   0x01
#define NET_ENC28J60_ESTAT_BUFFER 0x40
#define NET_ENC28J60_ECON1_RXEN   0x04
#define NET_ENC28J60_CHECK_PERIOD 5000UL

#define iwdg_init_ms(N) iwdg_init(IWDG_PRE_256,((N)/5))

#define pinLED PC13
#define pinSwitch PA15
#define ETH_RS_PIN PA0

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

IPAddress   ipdns(192, 168,   0, 1);
IPAddress      ip(192, 168,   0, 150);
IPAddress gateway(192, 168,   0, 1);
IPAddress  subnet(255, 255, 255, 0);

EthernetServer server = EthernetServer(80);

String cdata = "";
unsigned long timer;

void setup() {
  disableDebugPorts();
  Serial.begin(115200);
  pinMode(pinLED, OUTPUT);
  pinMode(pinSwitch, OUTPUT);
  digitalWrite(pinLED,LOW);
  digitalWrite(pinSwitch,LOW);
  eth_reset();
  iwdg_init_ms(4000);
  timer = millis();
}

void eth_reset() { 
  pinMode(ETH_RS_PIN, OUTPUT);
  digitalWrite(ETH_RS_PIN, LOW);
  delay(100);
  digitalWrite(ETH_RS_PIN, HIGH);
  pinMode(ETH_RS_PIN, INPUT);
  
  Ethernet.begin(mac, ip, ipdns, gateway, subnet);
  server.begin();
  Serial.print(F("WEB server is at "));
  Serial.println(Ethernet.localIP());

  Serial.print(F("DNS server is at ")); 
  Serial.println(Ethernet.dnsServerIP()); 
}

void loop() {
  iwdg_feed();
  Ethernet.maintain();
   
  if ((millis() - timer) > NET_ENC28J60_CHECK_PERIOD) {    

       // Enc28J60 is Enc28J60Network class that is defined in Enc28J60Network.h
       // readReg() subroutine must be moved from private to public members area in utility\Enc28J60Network.h
       // ENC28J60 ignore all incoming packets if ECON1.RXEN is not set
       uint8_t stateEconRxen = Enc28J60.readReg((uint8_t) NET_ENC28J60_ECON1) & NET_ENC28J60_ECON1_RXEN;
       // ESTAT.BUFFER rised on TX or RX error
       // I think the test of this register is not necessary - EIR.RXERIF state checking may be enough
       uint8_t stateEstatBuffer = Enc28J60.readReg((uint8_t) NET_ENC28J60_ESTAT) & NET_ENC28J60_ESTAT_BUFFER;
       // EIR.RXERIF set on RX error
       uint8_t stateEirRxerif = Enc28J60.readReg((uint8_t) NET_ENC28J60_EIR) & NET_ENC28J60_EIR_RXERIF;
       Serial.println("---REGS---");
       Serial.println(stateEconRxen,HEX);
       Serial.println(stateEstatBuffer,HEX);
       Serial.println(stateEirRxerif,HEX);
       if (!stateEconRxen || (stateEstatBuffer && stateEirRxerif)) {
         Serial.println ("ENC28J60 reinit");
//          Enc28J60.init(netConfig->macAddress);
         eth_reset();
       }
    timer = millis();
  }
  
  EthernetClient client = server.available();
  
  if (client) {    
    Serial.println(F("\n--Client Connected--\n"));
    cdata = "";
    Serial.println(cdata);
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.write(c);
        cdata.concat(c);
        if (cdata.indexOf("\r\n\r\n") > 0) {
          Serial.print(F("Buffer Length: "));
          Serial.println(cdata.length());
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println(F("Connection: close")); 
          if(cdata.indexOf("Analog") > 0) client.println(F("Refresh: 5"));
          client.println();
          client.println(F("<!DOCTYPE HTML>"));
          client.println(F("<html>"));
          client.println(F("<body>"));
          client.println(F("<br><br><center><H1>STM32F103C8T6 WEB Server</H1></center>"));

          if ( (cdata.indexOf("LED") > 0) || (cdata.indexOf("led") > 0) ) {
            digitalWrite(pinLED,!digitalRead(pinLED));
            if (digitalRead(pinLED)) client.println(F("<center><br><br><br><br><H1>LED OFF!</H1></center>"));
            else                     client.println(F("<center><br><br><br><br><H1>LED ON!</H1></center>")); 
            client.println(F("</body>"));
            client.println(F("</html>"));
            break;
          }

          if (cdata.indexOf("switch") > 0) {
            digitalWrite(pinSwitch,!digitalRead(pinSwitch));
            if (digitalRead(pinSwitch)) client.println(F("<center><br><br><br><br><H1>Switch ON! </H1></center>"));
            else                        client.println(F("<center><br><br><br><br><H1>Switch OFF!</H1></center>")); 
            client.println(F("</body>"));
            client.println(F("</html>"));
            break;
          }
          
          if(cdata.indexOf("Analog") > 0) {
            client.println(F("<table border=1 cellspacing=4 cellpadding=4>"));
            client.println(F("<tr><th>Analog</th><th>Value</th></tr>"));
            for (int analogChannel = 0; analogChannel < 4; analogChannel++) {
              int sensorReading = analogRead(analogChannel);
              client.print(F("<tr><td>"));
              client.print(analogChannel);
              client.print(F("</td><td>"));
              client.print(sensorReading);
              client.println(F("</td></tr>"));       
            }
            client.println(F("</table>"));
            client.println(F("</body>"));
            client.println(F("</html>"));
            break;
          }         
          
          client.println(F("<center><br><br><br><br><H1>Unknown Command!</H1></center>"));
          client.println(F("</body>"));
          client.println(F("</html>"));
          break;
        }
      }
    }
    client.stop();
    Serial.println(F("\n--Client Disconnected--"));
    timer = millis();
  }  
}

This is an updated and enhanced sketch from a previous post on a WEB server using both the BluePill (STM32F103C8T6) and the ENC28J60 module. It seems the UIPEthernet  library has changed since then. I had to add these lines to the code to define the correct pin settings:

#define BOARD_SPI1_NSS_PIN  PA4
#define BOARD_SPI1_SCK_PIN  PA5
#define BOARD_SPI1_MISO_PIN PA6
#define BOARD_SPI1_MOSI_PIN PA7

The source code has the IP address and network settings for the ENC28J60 ethernet module hard coded. Those settings may need changed for use on a different network.

The relay switch can be attached to any number of AC type low current devices and turned ON/OFF by accessing the WEB server. (The relay switch can possibly handle low current DC devices as well). I used a 2N3904 NPN transistor in my circuit to allow the STM32F103CT6 to turn the relay OFF and ON. This circuit also has a red LED to visually show when the relay is ON or OFF.

The WEB server accepts several ‘commands’:

http://192.168.0.150/led

http://192.168.0.150/analog

http://192.168.0.150/switch

‘LED’ toggles the internal LED On/Off, ‘analog’ displays the analog values of the first 4 analog pins and ‘switch’ toggles the relay On/Off.  So now you know how a Web Server on an STM32F103C8T6 can be used to control a Relay Switch.

Leave a Reply

Your email address will not be published.