I have designed and been using a motor controller via PID with feedback via encoder, and a nrf24 radio linked hand piece which allowed setting of rpm, on/off and a couple of other variables while mobile around the machine.
I have had issues with the PID due to all the other stuff the nano had to do and found myself going to direct writing to pin to control the motor and avoid PID issues.
ie
void computeOutput()
{
if (Input != Setpoint)
{
if (Input < Setpoint)
{
output = output + 1;
}
if (Input > Setpoint)
{
output = output - 1;
}
if (output <= 0)
{
output = 0;
}
if (output >= 255)
{
output = 255;
}
}
analogWrite(PWMpin, output);
}
I need to take this to the next level.
This is what I now propose:
Motor is a brushed 12v 200w wheel chair type motor. 40 rpm max after reduction.
Uno controls a BTS7960 based speed controller via PID
Uno and Wemos D1 mini are linked via SPI
Wemos is in AP mode and serves a page/pages to mobile device to select on/off, required rpm, auto on/off via position sensor, variable rate on/off, etc.
Wemos sends actual rpm and on/off status to mobile device.
Wemos receives data from gps module via software serial for variable rate according to ground speed.
Wemos reads induction sensor for auto motor on/off.
Wemos read current to motor via current sensor for esc protection and warn operator of overload.
Wemos sends PID Setpoint and esc enable to Uno
Reasons for doing this?
-
The custom made hand controller became redundant with the addition of new features.
-
[Using a Uno to just handle the PID and no more reduces the problems with PID loop interference from interrupt and serial functions.
/li] -
Using a Wemos to direct connect a mobile device via wifi allows almost infinite development of new functions.
So far I have got the Uno and Wemos talking to each other via SPI, the GPS is sending data to the Wemos via ss, the Wemos is able to be accessed via mobile device and serves basic html.
I am trying to decide if the Wemos or the Uno should have the task of reading the rpm of the motor. I am leaning towrds the Wemos due to problems I have experienced before with PID and interrupt functions.
I have no experience with the Wemos as a server and face a steep learning curve to write a sensible html or java interface for the mobile device.
I also struggle with sending data via SPI. I am currently sending strings as per example but of course I need to send integers or an array.
Here is the Wemos code in it's current rough form.
/*
Pin allocation table
D0
D1 (GPio5) Linkage sensor
D2
D3 (GPio0) Rx1 Gps
D4 (GPio2) Tx1 Gps
D5 (GPio14) Uno13
D6 (GPio12) Uno12
D7 (GPio13) Uno11
D8 (GPio15) Uno10
A0
*/
//-----------Serial Comms stuff--------------
#include <SoftwareSerial.h>
SoftwareSerial GPSserial(0, 2); //GPS Rx D3, Tx D4
//-------------SPI-----------------------
#include "SPISlave.h"
int spiTxArray[3];//0=rpm, 1=Setpoint, 2=isStarted.
byte spiTxArrayLen = 6;
int spiRxArray[3];
byte spiRxArrayLen = 6;
//------------Wifi Stuff------------------
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include "FS.h"
const char *ssid = "Seeder";
//const char *password = "";
ESP8266WebServer server(80);
void handleRoot() {
server.send(200, "text/html", "<h1>Seabrook Seeders
<a href='html_test.html'> test</a></h1>");
}
//---------------GPS Stuff------------------
#include <TinyGPS++.h>
static const uint32_t GPSBaud = 9600;
// The TinyGPS++ object
TinyGPSPlus gps;
unsigned long last = 0UL;
float groundSpeed;
bool gpsStatus = false;
//---------------Linkage sensor------
const int linkage = 3;
bool linkageSensor = false;
//--------------other stuff------------------
int Setpoint = 0;
bool isStarted = false;
bool cal = false;
bool variableRate = false;
void setup() {
GPSserial.begin(9600);
delay(1000);
Serial.setDebugOutput(true);
SPIFFS.begin();
Serial.begin(115200);
Serial.println();
Serial.print("Configuring access point...");
WiFi.softAP(ssid);// password parameter removed.
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.on("/", handleRoot);
// server.on("/html_test.html", html_test);
server.begin();
Serial.println("HTTP server started");
SPISlave.onData([](uint8_t * data, size_t len) {
String message = String((char *)data);
if(message.equals("Hello Slave!")) {
SPISlave.setData("Hello Master!");
} else if(message.equals("What speed are we doing?")) {
char answer[33];
//sprintf(answer,"Alive for %u seconds!", millis() / 1000);
sprintf(answer, "Ground speed is %u kph", int(groundSpeed));
SPISlave.setData(answer);
} else {
SPISlave.setData("Say what?");
}
Serial.printf("Question: %s\n", (char *)data);
});
// The master has read out outgoing data buffer
// that buffer can be set with SPISlave.setData
SPISlave.onDataSent([]() {
Serial.println("Answer Sent");
});
// status has been received from the master.
// The status register is a special register that bot the slave and the master can write to and read from.
// Can be used to exchange small data or status information
SPISlave.onStatus([](uint32_t data) {
Serial.printf("Status: %u\n", data);
SPISlave.setStatus(millis()); //set next status
});
// The master has read the status register
SPISlave.onStatusSent([]() {
Serial.println("Status Sent");
});
// Setup SPI Slave registers and pins
SPISlave.begin();
// Set the status register (if the master reads it, it will read this value)
SPISlave.setStatus(millis());
// Sets the data registers. Limited to 32 bytes at a time.
// SPISlave.setData(uint8_t * data, size_t len); is also available with the same limitation
SPISlave.setData("Ask me a question!");
pinMode(linkage, INPUT);
}
void loop() {
server.handleClient();
readGps();
debug();
getSetPoint();
readLinkageSensor();
getStartStop();
}
void getSetPoint()
{
// Setpoint = 20;
if (variableRate == true && cal == false)
{
if (gpsStatus == false)
{
Setpoint = spiRxArray[1];//ToDo
}
else Setpoint = round(((float)spiRxArray[1] / 10) * groundSpeed);
}
else Setpoint = spiRxArray[1];
}
void readLinkageSensor()
{
digitalRead (linkage);
}
void getStartStop()
{
if (spiRxArray[0] == 1) //start/stop button on.
{
if (spiRxArray[3] == 0) //height switch off.
{
isStarted = true;
}
if (linkageSensor == HIGH && spiRxArray[3] == 1) //if ping dist is less than dist set point and height switch is on.
{
isStarted = true;
}
else if (linkageSensor == LOW && spiRxArray[3] == 1)
{
isStarted = false;
}
}
else
{
isStarted = false;
}
}
void readGps()
{
// Dispatch incoming characters
while (GPSserial.available() > 0)
gps.encode(GPSserial.read());
if (gps.speed.isValid())
{
gpsStatus = true;
groundSpeed = (gps.speed.kmph());
}
else if (millis() - last > 5000)
{
if (gps.charsProcessed() < 10)
{
gpsStatus = false;
// spiTxArray[2] = 0;
}
// else spiTxArray[2] = 1;
last = millis();
}
}
void debug()
{
// Serial.print(" ground speed ");
// Serial.println(groundSpeed);
}