WebRTC Screen/Video Sharing

As many readers of this site can tell, I am a bit off topic with this article. None the less, I think those same readers will find it useful. The topic is WebRTC Screen/Video Sharing and shows how to communicate with another person (peer to peer) via video and audio using a Web browser.  This can be very useful to share a ‘screen’ or video and provide remote assistance / technical support.  Complete code and documentation is also provided to try it out (or view the documentation to see the features and if they are of interest).

As working from home becomes more prevalent these days, there is a greater need for this type of service. There are popular software products which offer some of these services, however,  perhaps you are like me and want to try/code these things yourself.  I get the features I want and I control  the (sometimes overlooked) security aspects of the service.

For those not acquainted with WebRTC, it is a means of communications between two peers. (RTC is an abbreviation for Real Time Communications) WebRTC allows for secure sharing of audio and video directly between those two peers, browser to browser (usually without anything in between).

For example, If someone has a problem/question on their computer (perhaps about the Arduino IDE) and you want to show them how something is done, get on the WebRTC Screen/Video sharing page with them. Then share  a screen and ‘walk’ them through the process in question.

WebRTC uses an API built into most modern day browsers.  Technically there are several protocols involved in WebRTC (signalling, ICE, STUN and/or TURN). For more specific information on WebRTC, see the WebRTC.org site.

Specifically, the Web application that I am showing in this article, uses the WebRTC API to allow two users to securely communicate via audio/video over a peer to peer connection in real time.  Each of the two ‘peers’ using this Web app would need to have a video device (Web camera), microphone and speakers or headphones.

Features for this Web app include audio, video and/or screen sharing, screen capture, video capture/replay/download, chat messages, file transfer and playing a local video to stream it to the remote peer.

Technically I am not using a ‘Web’ server (like Apache or Nginx) to serve the Web page. I have coded a Node.js server script (in Javascript) which functions as a Web server in that it handles the browser HTTPS Web page request and serves back the Web page.

Here is the code for  the Node Server:

const HTTPS_PORT      = 8080;
const fs              = require('fs');
const https           = require('https');
const WebSocket       = require('ws');
const WebSocketServer = WebSocket.Server;

const serverConfig = {
  key: fs.readFileSync('/etc/letsencrypt/live/yourdomain.com/privkey.pem'),
  cert: fs.readFileSync('/etc/letsencrypt/live/yourdomain.com/fullchain.pem'),
};

const handleRequest = function(request, response) {
  console.log('request received: ' + request.url);
  if (request.url.search('/webrtcvideo') == 0) {
    response.writeHead(200, {'Content-Type': 'text/html'});
    response.end(fs.readFileSync('index.html'));
  } else if(request.url === '/graph.js') {
    response.writeHead(200, {'Content-Type': 'application/javascript'});
    response.end(fs.readFileSync('graph.js'));
  } else if(request.url === '/webrtc.js') {
    response.writeHead(200, {'Content-Type': 'application/javascript'});
    response.end(fs.readFileSync('webrtc.js'));
  } else if(request.url === '/style.css') {
    response.writeHead(200, {'Content-Type': 'text/css'});
    response.end(fs.readFileSync('style.css'));
  }
};

const httpsServer = https.createServer(serverConfig, handleRequest);
httpsServer.listen(HTTPS_PORT, '0.0.0.0');

const wss = new WebSocketServer({server: httpsServer});

wss.on('connection', function(ws) {
  ws.on('message', function(message) {
    console.log('received: %s', message);
    wss.broadcast(message);
  });
});

wss.broadcast = function(data) {
  this.clients.forEach(function(client) {
    if (client.readyState === WebSocket.OPEN) client.send(data);
  });
};

console.log('Server running on https port:' + HTTPS_PORT);

Note: The script (and WebRTC) requires an SSL certificate to ensure secure communications. Make sure to replace the lines (with key: and cert: with valid certificate references). Note: The port serving the Web page is 8080 which can be changed to any port (not conflicting with other ports in use on the server).

The modules required for the node server can be installed via npm:

npm i ws fs  https

To start the node server, use the command:

node server.js

To access the WebRTC app, in addition to the node server, a STUN and/or TURN server is also required.  For the peers to communicate, they need to know how to contact each other and therefor need to know each others’ IP address.  The STUN or (TURN) server provides this info.  The TURN server is needed if one or both of the peers is behind a NAT. As mentioned earlier, for more specifics on how WebRTC works check the the  documentation on the  WebRTC.org site. There are public STUN servers available so no need to run your own.

However, if a TURN server is need, the coturn open source TURN server is the one I recommend.

The main functioning of this app is via the webrtc.js javascript code. There are two lines  in the webrtc.js script which must be edited and changed to replace the urls for the STUN/TURN server references.

var PeerConnectionConfig = {
  'iceServers': [
    { "urls": "turn:yourdomain.com:3478","username": "username", "credential": "secretcode" },
    { "urls": 'stun:yourdomain.com:3478'}
  ]
};

Also, if the port is changed in server.js, make sure to change it in the webrtc.js source to match also.  Once the node server is running and the changes are made to the webrtc.js script for the STUN/TURN server references, the WebRTC app is ready to go.

Using the Firefox or Chrome browser, navigate to its page:

https://yourdomain.com:8080/webrtcvideo

Replacing  yourdomain.com (and optionally the port) with the actual domain name running the node server script.

The browser will ask if it is OK to share your video and audio device, once approved, the Web Page will show up:

WebRTC Screen/Video Sharing

 

Click the ‘Show Functions’ / ‘Hide Functions’  button to show or hide all of the extra features available for this app. View or download the documentation (HERE) for an explanation of the features.

The complete source code ( a ZIP file which includes the documentation) , can be downloaded (HERE).

A Wireless WEB Server on an ESP8266

This is a short post which shows how to create a wireless WEB server on an ESP8266.  It uses a microcontroller, in this case an STM32F103C8T6 (BluePill )  to send commands and control the ESP8266

Here is what the wiring looks like:.

A Wireless WEB Server on an ESP8266

 

You can get the Fritzing code for this image here.

 

 

 

 

Here is the Arduino IDE source code to load into the STM32F103C8T6 (BluePill) to create a Wireless WEB Server on an ESP8266:

/* 

 From: earl@microcontrollerelectronics.com 

 *  PA9/PA10  Serial
 *  PA2/PA3   Serial1
 *  PB10/PB11 Serial2
 *  I2C1 SDA PB7
 *  I2C1 SCL PB6
 *  
 *            +-----------------[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]| 
             +-------------+---+---+---+-------------+
                           |   |   |   |

*/

String sbuffer = "";
char connectionId;
uint32_t timer = 0;

#define ESP8266_RST PB8

void setup() {
  pinMode(ESP8266_RST,OUTPUT);
  Serial.begin(115200);
  Serial2.begin(115200); 
  init_server();
}

void init_server() {
  digitalWrite(ESP8266_RST,LOW);
  delay(100);
  digitalWrite(ESP8266_RST,HIGH);
  sendData("AT+RST\r\n",5000,(String)"ready",1); 
  sendData("AT+GMR\r\n",5000,(String)"OK",1);
  sendData("AT+CWMODE=2\r\n",5000,(String)"OK",1);
  String cmd = "AT+CWSAP=\"ESP8266\",\"\",6,0\r\n";
  sendData(cmd,5000,(String)"OK",1);
  sendData("AT+CIFSR\r\n",5000,(String)"OK",1);
  sendData("AT+CIPMUX=1\r\n",5000,(String)"OK",1);
  sendData("AT+CIPSERVER=1,80\r\n",5000,(String)"OK",1); 
}

void get_serial_data() {
  while(Serial2.available()) {
    char c = Serial2.read();
    sbuffer.concat(c);
    Serial.write(c);
  }
}
 
void loop() {
  get_serial_data();
  
  if (sbuffer.indexOf("busy") != -1) init_server();
  if (sbuffer.indexOf("ERROR") != -1) init_server();
  
  if (sbuffer.indexOf("+IPD,") != -1) {
    timer = millis() + 1000;
    while (millis() < timer) get_serial_data();
    int pos = sbuffer.indexOf(",CONNECT");
    if (pos != -1) pos -= 1;
    if (sbuffer[pos] == 0x0d) connectionId = sbuffer[pos+2];   
    else                      connectionId = sbuffer[pos];
    Serial.println(connectionId,HEX);
    String webpage = "<!DOCTYPE HTML>\n<html>\n<body>\n<h1>Hello World!</h1>\n";
    webpage += "<script>setTimeout(function(){window.location.reload(1);},5000);</script>\n";
    webpage += "</body>\n</html>";
    String cipSend = "AT+CIPSEND=";
    cipSend += connectionId;
    cipSend += ",";
    cipSend += webpage.length();
    cipSend += "\r\n";
    sendData(cipSend,2000,(String)"OK",1);
    sendData(webpage,2000,(String)"SEND OK",1);
    String closeCommand = "AT+CIPCLOSE=";
    closeCommand += connectionId;
    closeCommand += "\r\n";    
    sendData(closeCommand,2000,(String)"OK",1);
  }
}
 
void sendData(String command, const int timeout, String msg, boolean flag) {
  Serial.println(command);
  Serial2.print(command);
  long int time = millis() + timeout;
  while(time > millis()) {
    get_serial_data();
    if (sbuffer.indexOf(msg) != -1) break;
  }
  if (sbuffer.indexOf(msg) == -1) {
    long int time1 = millis() + 500;
    while( time1 > millis()) {
      get_serial_data();
      if (sbuffer.indexOf(msg) != -1) break;
    }
  }
  get_serial_data();
  if (flag) sbuffer = "";
}

If you open the serial monitor on the Arduino IDE while the code is running you will see the commands being sent to the ESP8266 and its response. You can also watch what comes to its WEB site and what it sends out in return. This can be an interesting learning experience for the beginner.

This is a simple project to make but can be the start of a bigger project or added to another circuit to add wireless capabilities.

STM32F103C8T6 Solar Power System Monitoring

If you read my last post on Solar Power System  Monitoring with an Arduino you might be wondering if that circuit could be enhanced a bit. Say, change the LCD to an OLED and add wireless (WEB server) capability?

I was not happy to have to go out and look at the device when I wanted to see the info. So I did enhance it a bit. I changed the LCD to an OLED for more info in case I did happen to be where the device is and I added a wireless module (an ESP8266). The wireless module has a WEB server so I can just connect wireless via a browser and see the info.

Since the ESP8266 needs 3 Volts, I also changed the ‘arduino’ I was using. I switched to an STM32F103C8T6 (BluePill) since it can take 5 Volts and output 3 Volts.  This new circuit still uses the LM7805 voltage regulator to take the 14 Volt input and then output 5 Volts. It also keeps the SN75176BP for the RS485 protocol conversion.  If you have not read the previous post, you can find more details there on those components.

Here is what the circuit wiring looks like:

STM32F103C8T6 Solar Power System Monitoring

 

 

You can get the Fritzing code for this circuit here.

 

 

 

Here is what the circuit looks like (top and front side view):

STM32F103C8T6 Solar Power System MonitoringSTM32F103C8T6 Solar Power System Monitoring

 

 

 

 

 

 

The left side has the STM32F103C8T6 and the RJ11 connector. The right side has the ESP8266, the OLED, the SN75176BP and the LM7805. The LM7805 has a heat sink on it since it gets a bit hot. The STM32F103C8T6, ESP8266, OLED and  SN75176BP are all in sockets for easy (re)placement. This circuit has all the ‘messy’ wiring and soldering on the bottom (hidden from view).  In my previous post I still had all the components on a breadboard.

Here is the code to load into the STM32F103C8T6 (using the Arduino IDE):

/*

    PA9/PA10  Serial
    PB10/PB11 Serial2
    PA2/PA3   Serial1
    I2C1 SDA PB7
    I2C1 SCL PB6

               +-----------------[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]|
               +-------------+---+---+---+-------------+
                             |   |   |   |
*/

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306_STM32.h>

#define OLED_RESET PB4
Adafruit_SSD1306 display(OLED_RESET);

String sbuffer = "";
char connectionId;
#define MAXBUFFER 512
#define DE PB12
#define ESP8266_RESET PB8

uint32_t last_millis = 0;
uint32_t timer       = 0;
unsigned int bl      = 0;
float    volts       = 0.0;
int      amps        = 0;

unsigned char Buffer[MAXBUFFER + 1];
unsigned char SBuffer[MAXBUFFER + 1];

char data[256];
char DcV[10];

// Serial  -> host PC
// Serial1 -> RS485 SN75176
// Serial2 -> ESP8266

void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
  display.display();
  Serial.begin(115200);
  Serial2.begin(115200);
  Serial1.begin(19200);
  pinMode(DE, OUTPUT);
  digitalWrite(DE, LOW);
  display.clearDisplay();
  display.display();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println("Setting up ESP8266..");
  display.display();
  Serial.println("Setting up ESP8266...");
  init_server();
  Serial.println("Setup Done!");
  display.clearDisplay();
  display.display();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println("Setup Done!");
  display.display();
  last_millis = millis();
}

void init_server() {
  sendData("AT+RST\r\n", 5000, (String)"jump to run", 1);
  sendData("AT+RESTORE\r\n", 5000, (String)"ready", 1);
  sendData("AT+GMR\r\n", 5000, (String)"OK", 1);
  sendData("AT+CWMODE=2\r\n", 5000, (String)"OK", 1);
  String cmd = "AT+CWSAP=\"ESP8266\",\"\",6,0\r\n";
  sendData(cmd, 5000, (String)"OK", 1);
  sendData("AT+CIFSR\r\n", 5000, (String)"OK", 1);
  sendData("AT+CIPMUX=1\r\n", 5000, (String)"OK", 1);
  sendData("AT+CIPSERVER=1,80\r\n", 5000, (String)"OK", 1);
}

void get_serial_data() {
  while (Serial1.available() > 0) {
    if (bl > MAXBUFFER) bl = 0;
    Buffer[bl] = Serial1.read();
    ++bl;
    last_millis = millis();
  }
  while (Serial2.available() > 0) {
    char c = Serial2.read();
    sbuffer.concat(c);
    Serial.write(c);
  }
}

void loop() {
  get_serial_data();

  /*
    if (Serial.available()) {
      digitalWrite(DE,HIGH);
      Serial1.write(Serial.read());
      digitalWrite(DE,LOW);
    }
  */

  timer = millis() - last_millis;

  if ( (timer > 30) && (bl > 15) ) {
    for (int i = 0; i < bl; ++i) SBuffer[i] = Buffer[i];
    bl = 0;
    display.clearDisplay();
    display.display();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("Magnum Inverter");
    display.setTextSize(1);
    // Left in for Debugging
    //   sprintf(data,"%02X%02X%02X%02X%02X%02X%02X%02X",SBuffer[0],SBuffer[1],SBuffer[2],SBuffer[3],SBuffer[4],SBuffer[5],SBuffer[6],SBuffer[7],SBuffer[8]);
    sprintf(data, "S%02X F%02x M%02X R%02X", SBuffer[0], SBuffer[1], SBuffer[14], SBuffer[10]);
    display.println(data);
    volts = ( ((unsigned char)(SBuffer[2]) * 256) + (unsigned char)(SBuffer[3]) ) / 10.0;
    dtostrf(volts, 5, 2, DcV);
    amps = ((unsigned char)(SBuffer[4]) * 256) + (unsigned char)(SBuffer[5]);
    sprintf(data, "%s5Vdc %3dA", DcV, amps);
    display.println(data);
    display.display();
  }

  if (sbuffer.indexOf("busy") != -1) init_server();

  if (sbuffer.indexOf("+IPD,") != -1) {
    timer = millis() + 500;
    while (millis() < timer) get_serial_data();
    int pos = sbuffer.indexOf(",CONNECT");
    if (pos != -1) pos -= 1;
    if (sbuffer[pos] == 0x0d) connectionId = sbuffer[pos + 2];
    else                      connectionId = sbuffer[pos];
    Serial.println(connectionId, HEX);
    String webpage = F("<!DOCTYPE HTML>\n<html>\n<body>\n<H1>Magnum Inverter</H1>\n");

    // Left in for Debugging
    //   sprintf(data,"%02X%02X%02X%02X%02X%02X%02X%02X",SBuffer[0],SBuffer[1],SBuffer[2],SBuffer[3],SBuffer[4],SBuffer[5],SBuffer[6],SBuffer[7],SBuffer[8]);

    sprintf(data, "S%02X F%02x M%02X R%02X", SBuffer[0], SBuffer[1], SBuffer[14], SBuffer[10]);
    webpage += "<H2>";
    webpage += data;
    webpage += "</H2>";
    volts = ( ((unsigned char)(SBuffer[2]) * 256) + (unsigned char)(SBuffer[3]) ) / 10.0;
    dtostrf(volts, 5, 2, DcV);
    amps = ((unsigned char)(SBuffer[4]) * 256) + (unsigned char)(SBuffer[5]);
    sprintf(data, "%s5Vdc %3dA", DcV, amps);
    webpage += "<H2>";
    webpage += data;
    webpage += "</H2>";
    webpage += F("<script>setTimeout(function(){window.location.reload(1);},5000);</script>\n");
    webpage += F("</body>\n</html>");
    String cipSend = "AT+CIPSEND=";
    cipSend += connectionId;
    cipSend += ",";
    cipSend += webpage.length();
    cipSend += "\r\n";
    sendData(cipSend, 2000, (String)"OK", 1);
    sendData(webpage, 2000, (String)"SEND OK", 1);
    String closeCommand = "AT+CIPCLOSE=";
    closeCommand += connectionId;
    closeCommand += "\r\n";
    sendData(closeCommand, 2000, (String)"OK", 1);
  }
}

void sendData(String command, const int timeout, String msg, boolean flag) {
  Serial2.print(command);
  display.clearDisplay();
  display.display();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.println(command);
  display.display();
  long int time = millis() + timeout;
  while (time > millis()) {
    get_serial_data();
    if (sbuffer.indexOf(msg) != -1) break;
  }
  if (sbuffer.indexOf(msg) == -1) {
    long int time1 = millis() + 500;
    while ( time1 > millis()) {
      get_serial_data();
      if (sbuffer.indexOf(msg) != -1) break;
    }
  }
  get_serial_data();
  display.println(sbuffer);
  display.display();
  if (flag) sbuffer = "";
}

I am using the Adafruit GFX and SSD1306  libraries to control the OLED. The ESP8266 is just connected via a serial connection and sent commands to set it up as an access point and WEB server.

The wireless access point name is just set to ESP8266. You can change it to anything you want. The IP address is 192.168.4.1, so just go to that IP address in a browser after connecting wirelessly. The WEB page will refresh every 5 seconds.

My next step is to enhance the code to display more info as it still only outputs the codes like the LCD version does. Here is a picture (photography is not my forte) of the device on top of one of my Lithium Batteries (from Deeds Solar Solutions).

 

 

 

 

Solar Power System Monitoring with an Arduino

Sometimes, people wonder about the practical side of using an arduino. I do get asked what is the real use for some of the articles I post here.  Are these ‘gadgets‘ just toys or do they have a practical use?  Most people who ask though, are not that familiar with arduinos or microcontrollers in general.

So, this post is a partial answer to some of those questions and a place I can refer people to.  Although, if people were really curious, a search would show them a number of practical uses for arduinos and microcontrollers in general.

For example, I recently posted articles on using an arduino to communicate via the RS485 protocol here and here. Another post shows how to use an LCD (using only 3 pins) with an arduino here.

This post will show how to use the knowledge gained from those posts (plus a bit more) to build an arduino ‘gadget’ which can be used to monitor an inverter. Hence, the title of this post:  “Solar Power System Monitoring with an Arduino”.

An inverter is an essential part of a solar power system which uses sun light (solar energy) to produce electricity.

A solar power system (initial investment) can be quite expensive, depending on energy needs. Replacing parts is also expensive.  Monitoring what is happening with the solar energy conversion to electricity and its subsequent usage, is a very important part of the system to make sure it is being used efficiently and properly. If we can use an arduino for monitoring (which we can), we thus have a practical use for it (especially considering the price of buying commercial products versus do it yourself with an arduino).

I happen to have a Magnum inverter which is connected (and controlled) by an ME-ARC remote monitor. I was wondering if the ME-ARC is really needed and if it could be replaced with an arduino and an LCD screen. In my testing, I found out that the ME-ARC is not needed to actually run the inverter and it can be replaced with an arduino. What follows documents how it can be done.

The only connection into the ME-ARC is an RJ11 cable from the inverter. This RJ11 cable is the same type of cable used to connect telephones to wall jacks.

Since there is no external power cable, the RJ11 connection must be supplying both power (volts/ground) and data.  Further information is needed on how the remote gets power and data from the inverter. This information is in the Magnum Network Communications Protocol document found via a search.

From the document, we can see that the RJ11 cable does indeed provide power (14 Volts, Ground) and data. The protocol in use for data interchange is RS485. So we can use the information from my earlier posts to see how we can attach an arduino to the inverter. Note: The end of the document has a ‘Third Party Notes’ section which concerns  connecting your own device to the Magnum Network. How did they know we were going to connect an arduino ?  🙂

The RJ11 (4 wire) connection is wired like this:

Pin 1 - B (RS485 data)
Pin 2 - 14 Volts +
Pin 3 - Ground
Pin 4 - A (RS485 data)

Looking at the ‘front’ of the RJ11 connector (where it plugs in)  with the tab  at the top and wires going away to the back,  pin 1 is on the right side,  pin 4 is on the left side.

Since the arduino and the LCD need 5 volts to work and the inverter is sending 14 volts, we need a way to regulate the 14 volts down to 5 volts.

Here is the simple circuit to accomplish that.

LM7805 Voltage Regulator

 

Get the Fritzing code for that circuit image here.

 

 

LM7805 Circuit

This is what the circuit looks like when built.

 

 

 

 

I used an RJ11 splitter to attach both the ME-ARC and my arduino at the same time.

The splitter RJ11 Splitterwas connected to the back of the ME-ARC. The RJ11 cable from the inverter was plugged into one side of the splitter and the cable to the arduino was plugged into the other side of the splitter. Wiring that way allows viewing the ME-ARC and checking what it is showing versus what the arduino is showing. This is a good way to check that the code in the arduino is providing the same (accurate) results.

Since the communications from the inverter uses the RS485 protocol, we will use the wiring shown in a previous post which uses the SN75176BP IC for the protocol conversion.

If you haven’t taken the time to read that post, here are the wiring diagrams for the SN75176BP (left) and the complete circuit (below). The diagram shows RS485 communications between two arduinos, however, we can just pretend that one of the arduinos is the inverter. In fact, one of the arduinos was running code to emulate the inverter for testing purposes but I did not mention that in the original post.  Now you know. 🙂

SN75176BP RS485 Communications using Arduinos

Get the Fritzing code for that diagram here.  For connecting just one arduino to the actual inverter, the ‘A’ pin (from the RJ11 connector pin 4) connects to the ‘A’ pin of the SN75176BP and the ‘B’ pin (from the RJ11 connector pin 1) connects to the ‘B’ pin of the SN75176BP.

LCD Shift Register Circuit for an LCD

 

In hooking up the LCD to the arduino, we will also use the information from a previous post.  There we show how to use a shift register IC to minimize the number of pins used to connect the LCD to the arduino.

 

Now that we have all of the hardware details, we can hook it all up. The RJ11 splitter is connected to the ME-ARC. The RJ11 cable from the inverter is connected to one side of the splitter. The other side of the splitter is connected to the arduino circuit this way:

Pin 1 - 'B' pin of SN75176BP
Pin 2 - 14 Volts to input of the LM7805 circuit
Pin 3 - Ground to Ground of the LM7805 circuit
Pin 4 - 'A' pin of SN75176BP

Here is what it looks like all attached and working:

Solar Power System Monitoring with an Arduino

From the picture, you can see the RJ11 cable (on the right) dropped down from the splitter on the ME-ARC. I have an RJ11 female/female connector to the cable and to an RJ11 connector which breaks out the 4 wires connecting to the arduino and the voltage regulator circuits.

You are probably wondering about the output on the LCD? Since the LCD has only 16 characters x 2 lines, it has limited space to display things. The first line has ‘codes’ for what it is displaying:

S40 - Status 40 (inverting)
F00 - Fault (none)
M73 - Inverter Model  (MS4448PAE)
R33 - Revision 33 (Inverter revision)

The 2nd line displays the DC volts and  amps being used just like the ‘Meter’ on the ME-ARC. The code in the arduino gets these values from a data packet sent by the inverter every 100 milliseconds. The Magnum Network Communications Protocol document (mentioned earlier) details the communications and values being sent.

Here is the arduino code so you can see how the arduino captures the inverter data, waits for the 100 millisecond ‘gap’ and then decodes and displays the values.

/*
RS485 Communications to a Magnum Inverter
from earl@microcontrollerelectronics.com
*/

#include <SoftwareSerial.h>
#include <LiquidCrystal595.h> 

#define MAXBUFFER 512
#define DE 7

LiquidCrystal595 lcd(3,4,5);   // datapin, latchpin, clockpin
SoftwareSerial mySerial(8,9);  // RX, TX

uint32_t last_millis = 0;
uint32_t timer       = 0;
unsigned int bl      = 0;
float    volts       = 0.0; 
int      amps        = 0;

unsigned char Buffer[MAXBUFFER+1];

char data[256];
char DcV[10];

void setup() {
//  Serial.begin(19200);
  mySerial.begin(19200);
  pinMode(DE,OUTPUT);
  digitalWrite(DE,LOW);
  lcd.init(3,4,5);
  lcd.setLED2Pin(HIGH);        // Backlight On
  lcd.begin(16,2);             // 16 characters, 2 rows
  lcd.clear();
  lcd.print("No Data yet!");
  last_millis = millis();
}

void loop() {
/*
 if (Serial.available()) {
   digitalWrite(DE,HIGH);
   mySerial.write(Serial.read());
   digitalWrite(DE,LOW);
 }
*/
 while (mySerial.available() > 0) {
  if (bl > MAXBUFFER) bl = 0;    
  Buffer[bl] = mySerial.read();
  ++bl;
  last_millis = millis();
 }
 timer = millis() - last_millis;
 if ( (timer > 30) && (bl > 15) ) {
// Left in for Debugging
//   sprintf(data,"%02X%02X%02X%02X%02X%02X%02X%02X",Buffer[0],Buffer[1],Buffer[2],Buffer[3],Buffer[4],Buffer[5],Buffer[6],Buffer[7],Buffer[8]);
   lcd.setCursor(0,0);
   sprintf(data,"S%02X F%02x M%02X R%02X",Buffer[0],Buffer[1],Buffer[14],Buffer[10]);
   lcd.print(data);
   volts = ( ((unsigned char)(Buffer[2]) * 256) + (unsigned char)(Buffer[3]) ) / 10.0;
   dtostrf(volts,5,2,DcV);
   amps = ((unsigned char)(Buffer[4]) * 256) + (unsigned char)(Buffer[5]);
   sprintf(data,"%s5Vdc %3dA",DcV,amps);
   lcd.setCursor(0,1);
   lcd.print(data);
   bl = 0;
 }
}

One serial port is used for communications to the inverter and another is used to optionally communicate with a PC and get commands to send to the inverter. Note: The code for transmitting data to the inverter is there but commented out.  I am using the Software Serial library (to emulate an additional serial port) because the Arduino Nano I am using for this project only has one serial port. The other library (LiquidCrystal595) is needed to control the LCD with only 3 pins. Make sure, if you use that library, to patch it according to the comments in my post about it mentioned above and referenced again here.

Since the inverter sends a data packet every 100 milliseconds, we can check for that ‘gap’ (no data being sent) and know that we have a complete packet.  However,  since it will take some time to run the code to display on the LCD we need to pick a ‘safe’ value for checking that timing. You will notice in the code:

timer = millis() - last_millis;
if ( (timer > 30) && (bl > 15) ) {

the timer holds the number of milliseconds since the last input byte from the inverter. I chose a ‘safe’ value of 30 (milliseconds) which should give enough time to display on the LCD and get back to checking for inverter data.

The next step would be to test sending data to the inverter as does the actual ME-ARC remote. Only then would we have something to be able to totally replace the ME-ARC.

So now you can see that there are ‘building blocks’ (so to speak) in my posts which used together can produce something practical, Solar Power System Monitoring with an Arduino.

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.

SN75176 RS485 Communications between two Arduinos

Previously, I wrote about RS485 communications between two Arduinos using a MAX485 IC. The link for that post is HERE.  It showed one Arduino as a tranmitter and the other as a receiver. This post is very similar, however, I am using a different IC, the SN75176BP. This post is about SN75176 RS485 communications between two Arduinos which allows either one to transmit and receive at the same time.

The SN75176 IC is also an RS485 transceiver like the MAX485 however it is full duplex where as the MAX485 is only half duplex. Full duplex allows sending and receiving at the same time.

Here is the pin layout for the SN75176BP IC which I am using:

SN75176 RS485 Transceiver

 

 

 

 

 

This chart shows the function of each pin of the SN75176:

      SN75176
   +----U-----+
RO | 1      8 | Vcc
RE | 2      7 | B
DE | 3      6 | A
DI | 4      5 | Gnd
   +----------+

RO  - Receiver out
RE  - Receiver enable (enabled when this pin is LOW)
DE  - Driver   enable (enabled when this pin is HIGH)
DI  - Driver   in (the transmitter pin)
GND - Ground (0V)
A   - Connect to pin A of the other 485 IC
B   - Connect to pin B of the other 485 IC
Vcc - Power (Vcc)

Using two Arduinos, each with an SN75176BP, we can establish two way communications:

Serial / USB <-> Arduino <-> SN75176  (RS485) <-> SN75175 (RS485) <-> Arduino <-> Serial / USB
SN75176 RS485 Communications between two Arduinos

SN75176 RS485 Communications between two Arduinos

The Fritzing diagram source is HERE in case you would like to modify it.  Note that the RE (Receiver Enable) pin is wired permanently low. The DE (Driver Enable) pin is turned on (high) only when data is sent.

The Arduino UNO that is used here only has one serial device, so the software serial library is used to add another serial device on pins 8 and 9. The software serial port is used to communicate (via RS232) to the SN75176 and the built in serial port (via USB) is used to communicate (via RS232) with the host computer the Arduino is attached to.

Load the following code into each  Arduino:

/*
Arduino Sketch to allow RS232/RS485 communications
via two Arduinos with attached SN75176 ICs.

From: earl@microcontrollerelectronics.com
*/

#include <SoftwareSerial.h>

SoftwareSerial mySerial(8, 9); // RX, TX
#define DE 7

void setup(){
  Serial.begin(115200);
  mySerial.begin(115200);
  pinMode(DE,OUTPUT);
  digitalWrite(DE,LOW);
}

void loop() {
  if (mySerial.available()) Serial.write(mySerial.read());
  if (Serial.available()) {
    digitalWrite(DE,HIGH);
    mySerial.write(Serial.read());
    digitalWrite(DE,LOW);
  }
}

Attach a serial connection to each Arduino and data can be sent back and forth thru the  connection between the two SN75176 ICs.  Use my handy updated Python serial communications program for that purpose:

#!/bin/env python
#Python Serial Communications Script
#from earl@microcontrollerelectronics.com

import serial,sys,glob,select

def scan_ports(pat):
  scan = glob.glob(pat)
  if (len(scan) == 0): return("")
  else:                return(scan[0])

serport = ""
rate    = "115200"

if (len(sys.argv) > 1):
 l = len(sys.argv) - 1
 while(l>0):
   if (sys.argv[l][0] == '/'): serport = sys.argv[l]
   else:                       rate    = sys.argv[l]
   l = l - 1

if (serport == ""): serport = scan_ports("/dev/ttyACM*")
if (serport == ""): serport = scan_ports("/dev/ttyUSB*")
if (serport == ""):
  print("Unable to find any ports scanning for /dev/[ttyACM*|ttyUSB*]") 
  sys.exit()

ser = serial.Serial(port=serport,baudrate=rate,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,bytesize=serial.EIGHTBITS,timeout=1)

print("connected to: ",ser.portstr," ",ser)

while True:
  try:
    tr = ser.inWaiting()
    if tr > 0:
      print(ser.read(tr).decode("utf-8"),end='')
#     for x in line: print (x.encode('hex'),end='')
      sys.stdout.flush()
  except KeyboardInterrupt:
    sys.exit()
  except:
    pass
  while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:
#   line = sys.stdin.readline()
#   for x in line: print (x.encode('hex'),end='')
#   print
    ser.write(sys.stdin.readline().encode("utf-8"))

ser.close()
sys.exit()

Once this is working, you have a ‘prototype’ to emulate other equipment or use ‘half’ (one Arduino and one SN75176) to attach to other devices using the RS485 interface such as Modbus RTU.

Use An Arduino Leonardo and a Button for a USB Foot Pedal

It is possible to use an Arduino Leonardo and a Button for a USB Foot Pedal.  Here is the story on how one was built.

A long time ago, my daughter used to transcribe audio cassette tapes using a transcribing machine. It had a nice foot pedal to start, stop and rewind the cassette tape. Recently, she decided to get back into transcribing. Instead of audio from cassette tapes, she is transcribing MP3 audio files on the computer. She choose a software program called ‘Express Scribe Transcription Software’ from NCH Software for that purpose. It allows her to use any sort of word processing software to key in the text transcription while she listens to the audio it plays. Express Scribe allows her to map keys, which it intercepts, to stop and restart playing the audio. A nice feature that it has, is that it automatically goes back several seconds when stopped so no ‘rewind’ is really needed.

Here is a screen shot of what that software looks like:

Express Scribe Transcription Software
The Express Scribe Software also works with several different USB foot pedals.  The USB foot pedal acts as an extra keyboard and sends keystrokes when the pedal is pressed. If you have ever done any transcribing, you will know how handy using your foot is, versus using the keyboard for stopping and starting the audio.

Rather than buying a USB foot pedal, Dad was asked if there was an Arduino solution. Dad of course took up the challenge.

The first step in the process was to set up a prototype of the process. An Arduino Leonardo was chosen (due to Dad having several around) and due to the Leonardo being able to plug into the computer and function as a USB keyboard.

Every USB device is assigned a device class, which defines what exactly its general purpose is. The Leonardo can  be programmed  to act  like a mouse, keyboard or other HID (Human Interface Device) class USB device, so it is just right for this project.

A simple circuit was constructed to test the functionality of the Leonardo sending keystrokes when a button is pressed.

Here is the circuit (just a basic button circuit):

 

You can get the Fritzing code used to create that diagram HERE.

The Express Scribe software was configured to use the ` key for stopping playback (and rewinding) and the Tab key was configured to start the playback. So the Arduino Leonardo sketch was coded to send those keys when a button was pressed.

Here is the sketch (i.e. the code which is compiled via the Arduino IDE and loaded into the Leonardo):

#include "Keyboard.h"

const int buttonPin = 4;
int previousButtonState = HIGH;
int counter = 0;

void setup() {
  pinMode(buttonPin, INPUT);
  Keyboard.begin();
}

void loop() {
  int buttonState = digitalRead(buttonPin);
  if ((buttonState != previousButtonState)
      && (buttonState == HIGH)) {
    counter++;
    if (counter == 1) {
     Keyboard.print("`");
    }
    else {
     Keyboard.print("\t");
     counter = 0;
    }
    delay(500);`
  }
  previousButtonState = buttonState;
}

You will notice that only one button is used. There is not a separate button for stop and start. There is really no need. The sketch keeps track of what comes next. Pressing the button alternates between sending the Stop and Start keystrokes. Since the software takes care of the rewind (on stop) there is no need for that function via the button.

That code and the sample circuit was plugged into the PC via a USB cable to the Arduino Leonardo for testing and worked very well. The next step was to build something that could function as a foot pedal (i.e. allowing the foot to press the button).

An important consideration in this project is that whatever is used as a ‘button’ for the foot pedal be easy to push and still function as a ‘button’.   Here are some pictures of the large ‘gaming’  type buttons that works well for our purpose:

 

 

 

 

 

 

 

To use an Arduino Leonardo and a Button for a USB Foot Pedal we also needed something to house the Arduino and keep the button stable on the floor.  Here is where the brother (or son depending on who’s view you are looking from) comes into the picture.   The brother is adept at wood work so he was tasked with coming up with that.

RS485 Arduino Communications

There are various ways for two Arduinos to communicate information. A common way is serially via RS232. However, RS232 has distance limitations. If there is a need to serially communication over a longer distance consider using RS485 instead.  A couple of MAX485 ICs can facilitate RS485 Arduino communications over that longer distance.

Two MAX485 ICs connected together will take RS232 signals and perform the voltage level conversions required to turn them into RS485 signals and back again to RS232 signals. That will allow two Arduinos to communicate serially over a longer distance.

This chart shows the specific pin functions on the MAX485:

      MAX485
   +----U-----+
RO | 1      8 | Vcc
RE | 2      7 | B
DE | 3      6 | A
DI | 4      5 | Gnd
   +----------+

RO  - Receiver out
RE  - Receiver enable (enabled when this pin is LOW)
DE  - Driver   enable (enabled when this pin is HIGH)
DI  - Driver   in (the transmitter pin)
GND - Ground (0V)
A   - Connect to pin A of the other 485 IC
B   - Connect to pin B of the other 485 IC
Vcc - Power (Vcc)

Here is how to connect the two Arduinos using two MAX485 ICs:

Max485 Arduino Communications

Get the Fritzing source HERE.  5V is used in this simple demonstration. For longer distances a higher voltage will be needed to power the MAX485. Also the BAUD rate will need to be adjusted (the longer the distance, the slower the speed).

This is the source code for the Transmitter:

/* Arduino Transmitter */
#define LED_PIN 13
#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); 
// RX on digital pin 10
// TX on digital pin 11

int i = 0;
void setup() {
  Serial.begin(57600);
  mySerial.begin(57600);
}

void loop() {
  Serial.println(i);
  mySerial.println(i);
  digitalWrite(LED_PIN, !digitalRead(LED_PIN));
  delay(1000);
  i++;
}

This is the source code for the Receiver:

/* Arduino Receiver */

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup() {
  Serial.begin(57600);
  mySerial.begin(57600);
}

void loop() {
  if (mySerial.available()) {
    Serial.write(mySerial.read());
  }
/*
  if (Serial.available()) {
    mySerial.write(Serial.read());
  }
*/
}

Here is what it looks like (messy wiring and all):

MAX485 ArduinoMAX485 Arduino

Nuttx on the STM32F103C8T6

Wouldn’t it be  C 🙄 🙄 L to have a Linux prompt on the STM32F103C8T6.  Not likely, however, Nuttx on the STM32F103C8T6 is about as close as you will get.

Nuttx is a RTOS which runs on a wide range of microcontrollers including the STM32F103C8T6.

To compile Nuttx for the STM32F103C8T6, a development environment needs set up with the right tools. My development OS of choice is a Linux distribution called Fedora (Fedora 29 is the current version at the time of this post).

To install the tools needed, I used these commands:

dnf -y install arm-none-eabi-binutils-cs arm-none-eabi-gdb arm-none-eabi-gcc-cs-c++ arm-none-eabi-newlib arm-none-eabi-gcc-cs
dnf -y install git
dnf -y install flex bison gperf automake autoconf m4 libtool ncurses ncurses-devel

The first DNF command installs the ARM development tools needed to compile Nuttx for the ARM based STM32F103C8T6. The second command installs the GIT tool used to grab the source code. The third command installs other development tools necessary for the compile process.

To begin the process, create a directory to store the downloaded source and tools:

mkdir /temp/
mkdir /temp/nx
cd /temp/nx

The Nuttx source code is downloaded with these git commands:

git clone https://bitbucket.org/nuttx/nuttx.git nuttx
git clone https://bitbucket.org/nuttx/apps.git apps
git clone https://bitbucket.org/nuttx/tools.git tools

The Nuttx configuration process needs the  kconfig-frontends installed via these commands:

cd /temp/nx/tools/kconfig-frontends/
./configure
aclocal
autoconf
./configure
make

The reason for the aclocal and autoconf is to update the configuration files due to the newer versions of Fedoras’ development tools.

Next, create the Nuttx specific compile for the STM32F103C8T6 via these commands:

cd /temp/nx/nuttx/tools
./configure.sh stm32f103-minimum/nsh

That last command should have set up everything correctly for the compile. however,  this next step allows verification of all settings and options.

cd  /temp/nx/nuttx
make menuconfig

A menu will appear like this:

Nuttx Configuration Menu

 

 

 

 

Optionally, verify the build setup and other options. Save and Exit when done.

This last step compiles the code and creates the nuttx.bin binary file to load into the STM32F103C8T6.

cd /temp/nx/nuttx/
make

Attach the STM32F103C8T6 to a USB/Serial converter to load the Nuttx binary image:

STM32F103C8T6 with CP2102

To load Nuttx on the STM32F103C8T6 va the serial to USB converter, I used the stm32flash utility downloaded from  HERE.  Use these commands to install it:

tar -xvf stm32flash-0.5.tar.gz
cd stm32flash/
make
make install

Use this command to load the nuttx.bin file into the STM32F103C8T6:

/stm32flash -w /temp/nx/nuttx/nuttx.bin -v -g 0x0 /dev/ttyUSB0

Once loaded with the nuttx binary, the STM32F103C8T6 is ready to send the nsh (NuttShell) prompt. Connect your PC to the USB/Serial converter port with any serial communications program (115200 baud)  (like MINICOM ) and press the reset button.

Even the linux screen program can be used:

screen /dev/ttyUSB0 115200

I  like using  my Python serial program  (documented here) via the command:

./py_ser.py 115200

For more info on Nuttx and what can be done with it, check out the Nuttx.com site HERE.

Turn an STM32F103C8T6 (BluePill) into an STLink Programmer

It is relatively easy to turn an STM32F103C8T6 (BluePill) into an STLink programmer. All that is needed is a copy of the STLink firmware and a means to flash it. Do a search for STLinkV2.J16.S4 and download it. Flash that binary file into the STM32F103C8T6.

See my other post HERE on the STM32F103C8T6 as it mentions several methods to program (flash) it.

I have several ST-Link programmers so I used the stlink utilities program called st-flash and  flashed it via:

st-flash --debug write STLinkV2.J16.S4.bin 0x8000000

The new STM32F103C8T6 STLink can then be upgraded to the latest firmware version from https://www.st.com.

Here is the wiring diagram on how to connect the new STM32F103C8T6 STLink programmer to another STM32F103C8T6 to program it:

BluePill as an Stlink ProgrammerDownload the Fritzing Diagram from HERE. Actually, the capacitor and 4k7 resistors on the right side of the diagram are not needed (it works fine without them). It also works without the NRST to B0 jumper.

The minimal wiring looks like this:

STM32F103C8T6 as an ST-LINK

Load more