I'm writing a program which reads MPU9250's accelerometer&gyro data using MPU9250's internal FIFO and serves web interface.
Without web access, everything is fine.
However, on the exact timing of web request, bytes read on that timing is changed.
My current code just simply shows template webpage and prints IMU accelerometer value only if its magnitude is greater than 2.5. So, if I do not touch MPU9250 sensor, its value should be between 0.9~1.1. However, it prints some wrong values greater than 2.5 when the webpage is refreshed (currently auto-refreshing in 0.5 seconds ) although MPU9250 sensor is not touched at all.
I'm using LOLIN D32 PRO board. and MPU9250 is connected its default VSPI bus.
I read MPU 9250's FIFO data on arduino's loop function as follows,
while( readBytes = spiread_fifo( buffer + readBytes_all / 2 ) )
readBytes_all += readBytes;
if ( readBytes_all == 0 )
return;
if( digitalRead( 4 ) == 1 ){
int x = buffer[ 0 ];
int y = buffer[ 1 ];
int z = buffer[ 2 ];
double m = sqrt( x * x + y * y + z * z ) / 2048;
if( m > 2.5 )
Serial.println( m );
and spiread_fifo is defined as follows
int spiread_fifo( volatile short * buffer ) {
int fifo_len = spiread( 0x72 ) * 256 + spiread( 0x73 );
if ( fifo_len > 512 )
return -1;
if ( fifo_len == 0 )
return 0;
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x74 | 0x80 );
MPU9250.transfer( 0x00 ); // if I use SPI CLOCK more than 8~12Mhz, it gives me duplicated byte at the beginning. So just drop one of them.
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
for ( int i = 3; i < fifo_len / 2; i++ )
MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
return fifo_len;
}
If I do not touch or exert any force on MPU9250, serial console should be silent and it actually does if there's no ESP32's webserber access. However it gives some random value on the timing of webpage access, like follows. ( single value per single webpage access )
15.53
16.11
15.60
13.59
16.86
2.55
2.55
3.85
3.85
3.37
6.79
2.63
2.56
5.80
10.18
5.88
3.65
5.80
5.48
2.95
4.01
4.01
3.10
2.90
3.17
9.31
14.97
7.08
16.29
which are totally abnormal values.
My guesses are,
strong RF signal affects SPI bus so signals are altered.
wifi TX routine is called during MPU9250 FIFO readout process and it causes data drop during FIFO read.
Whatever the reason is, I don't know how to fix the problem.
Any possible causes/solutions would be appreciated.
Belows are my current wiring. However, nothing's special. MPU9250 is connected to default VSPI port and INT pin is connected to GPIO34. other remaining connection is not used.
PICTURE 1
PICTURE 2
full source codes below for your reference.
MAIN CODE :
extern volatile int cnt;
volatile short* buffer;
volatile short* buffer2;
volatile unsigned long *timestamp;
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
void printHex( int num, int precision) {
char tmp[16];
char format[128];
sprintf(format, "%%.%dX ", precision);
sprintf(tmp, format, num);
if ( strlen( tmp ) > precision + 1 ) {
int l = strlen( tmp ) - precision - 1;
for ( int i = 0; i < precision + 2; i++ ) {
tmp[ i ] = tmp[ i + l ];
}
}
Serial.print(tmp);
}
void setup() {
Serial.begin( 2000000 );
Serial.println( "Turning on...." );
buffer = ( short * )ps_malloc( 1000000 );
buffer2 = ( short * )ps_malloc( 1000000 );
BUTTONSetup();
MPU9250Setup();
OTASetup();
WEBSERVERSetup();
Serial.println( "Setup finished." );
}
void loop() {
OTAHandle();
WEBSERVERHandle();
int readBytes = 0, readBytes_all = 0;
while( readBytes = spiread_fifo( buffer + readBytes_all / 2 ) )
readBytes_all += readBytes;
if ( readBytes_all == 0 )
return;
if( digitalRead( 4 ) == 1 ){
int x = buffer[ 0 ];
int y = buffer[ 1 ];
int z = buffer[ 2 ];
double m = sqrt( x * x + y * y + z * z ) / 2048;
if( m > 2.5 )
Serial.println( m );
BUTTONHandle();
}
}
MPU9250 CODE :
#include <SPI.h>
#define SCK 18
#define MISO 19
#define MOSI 23
#define SS 5
#define INT 34
SPIClass MPU9250( VSPI );
SPISettings settingsA( 1000000, MSBFIRST, SPI_MODE3 );
SPISettings settingsB( 20000000, MSBFIRST, SPI_MODE3 );
volatile int cnt = 0;
void IRAM_ATTR onInterrupt() {
portENTER_CRITICAL_ISR(&mux);
cnt++;
portEXIT_CRITICAL_ISR(&mux);
}
void spiwrite( byte a, byte b ) {
MPU9250.beginTransaction( settingsA );
digitalWrite( SS, LOW );
MPU9250.transfer( a );
MPU9250.transfer( b );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
}
byte spiread( byte a ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( a | 0x80 );
byte r = MPU9250.transfer( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
return r;
}
int spiread_fifo( volatile short * buffer ) {
int fifo_len = spiread( 0x72 ) * 256 + spiread( 0x73 );
// fifo_len += 12;
// fifo_len = fifo_len / 12 * 12;
if ( fifo_len > 512 )
return -1;
if ( fifo_len == 0 )
return 0;
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x74 | 0x80 );
MPU9250.transfer( 0x00 ); // if I use SPI CLOCK more than 8~12Mhz, it gives me duplicated byte at the beginning. So just drop one of them.
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
for ( int i = 3; i < fifo_len / 2; i++ )
MPU9250.transfer16( 0x00 );
// for( int i = fifo_len / 2 + 1; i < fifo_len; i++ )
// buffer[ i ] = 0;
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
return fifo_len;
}
void spiread_raw( volatile short * buffer ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x3b | 0x80 );
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
}
void spiread_raw_gyr( volatile short * buffer ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x43 | 0x80 );
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
}
void spiread_raw_accgyr( volatile short * buffer ) {
MPU9250.beginTransaction( settingsB );
digitalWrite( SS, LOW );
MPU9250.transfer( 0x3b | 0x80 );
for ( int i = 0; i < 3; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
MPU9250.transfer16( 0x00 );
for ( int i = 3; i < 6; i++ )
buffer[ i ] = MPU9250.transfer16( 0x00 );
digitalWrite( SS, HIGH );
MPU9250.endTransaction();
for ( int i = 0; i < 12; i++ )
__asm__ __volatile__ ("nop\n\t");
}
void MPU9250Setup(){
pinMode( SS, OUTPUT );
pinMode( SCK, OUTPUT );
pinMode( MOSI, OUTPUT );
pinMode( INT, INPUT_PULLUP );
pinMode( MISO, INPUT );
pinMode( 4, INPUT );
MPU9250.begin( SCK, MISO, MOSI, SS ); //CLK,MISO,MOIS,SS
attachInterrupt( digitalPinToInterrupt( INT ), onInterrupt, FALLING );
spiwrite( 0x68, 0x07 );
spiwrite( 0x6A, 0x55 ); // FIFO_EN = 1, FIFO_RST = 1;
spiwrite( 0x19, 0x00 ); // SMPLRT_DIV = 0
spiwrite( 0x1B, 0x18 ); // GYRO_FS_SEL = 3, Fchoice_b = 0
spiwrite( 0x1C, 0x18 ); // ACCEL_FS_SEL = 3
spiwrite( 0x1D, 0x08 ); // accel_fchoice_b = 1
// spiwrite( 0x23, 0x78 ); // TEMP_OUT = 0, GYRO_XOUT = 1, GYRO_YOUT = 1, GYRO_ZOUT = 1, ACCEL = 1
// spiwrite( 0x23, 0x79 ); // TEMP_OUT = 0, GYRO_XOUT = 0, GYRO_YOUT = 0, GYRO_ZOUT = 0, ACCEL = 1
spiwrite( 0x23, 0x08 ); // TEMP_OUT = 0, GYRO_XOUT = 0, GYRO_YOUT = 0, GYRO_ZOUT = 0, ACCEL = 1
spiwrite( 0x37, 0x10 ); // INT_ANYRD_2CLEAR = 1
spiwrite( 0x38, 0xC1 ); // ACTL = 1, OPEN = 1, RAW_RDY_EN = 1
spiwrite( 0x1A, 0x07 ); // FIFO_MODE = 0, EXT_SYNC_SET = 0, DLPF_CFG = 7
}
OTA firmware CODE :
#include <WiFi.h>
#include "esp_wifi.h"
#include <ESPmDNS.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
const char* ssid = "XXXXXX";
const char* password = "XXXXXX";
void OTASetup(){
delay( 100 );
esp_wifi_set_max_tx_power( -100 );
WiFi.mode( WIFI_STA );
WiFi.setHostname( "LOLIN_D32_PRO_Sunkyue" );
delay( 100 );
WiFi.begin( ssid, password );
while( WiFi.waitForConnectResult() != WL_CONNECTED ){
Serial.println( "Connection Failed! Rebooting..." );
delay( 10 );
ESP.restart();
}
ArduinoOTA.setPort(53232);
ArduinoOTA.setHostname("LOLIN_D32_PRO_Sunkyue");
ArduinoOTA.setPassword("XXXXXX");
ArduinoOTA
.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
})
.onEnd([]() {
Serial.println("\nEnd");
})
.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
})
.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void OTAHandle(){
ArduinoOTA.handle();
}
WEBSERVER CODE :
#include <WiFiClient.h>
#include <WebServer.h>
WebServer server(80);
void handleRoot() {
char temp[400];
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;
snprintf(temp, 400,
"<html>\
<head>\
<meta http-equiv='refresh' content='0.5'/>\
<title>ESP32 Demo</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>Hello from ESP32!</h1>\
<p>Uptime: %02d:%02d:%02d</p>\
<img src=\"/test.svg\" />\
</body>\
</html>",
hr, min % 60, sec % 60
);
server.send(200, "text/html", temp);
}
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}
void drawGraph() {
String out = "";
char temp[100];
out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
out += "<g stroke=\"black\">\n";
int y = rand() % 130;
for (int x = 10; x < 390; x += 10) {
int y2 = rand() % 130;
sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
out += temp;
y = y2;
}
out += "</g>\n</svg>\n";
server.send(200, "image/svg+xml", out);
}
void WEBSERVERSetup(){
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
server.on("/", handleRoot);
server.on("/test.svg", drawGraph);
server.on("/inline", []() {
server.send(200, "text/plain", "this works as well");
});
server.onNotFound(handleNotFound);
server.begin();
}
void WEBSERVERHandle(){
server.handleClient();
}
I can see two solutions for a uni-processor solution(since you have not stated the final goals one may be more optimal than the other):
If it is ok to miss approximately 15-25 ms of data(a tap of the sensor should still register,) Change the spiread_fifo(...) to spiread_raw_accgyr(buffer) in the loop(). This will read the current values into the buffer at the time of the execution. This would definitely not be appropriate for position calculations or calculations of seismic events.
Read and analyze the required data in an interrupt routine(either the hardware pin from the MPU9250, or a timer), since the web client is much more forgiving of delays(within reason).
Either way, the calculations should be optimized to reduce the instruction count(the ESP32 does not have an FPU, so all of the floating point operations must be emulated in software):
The lines:
double m = sqrt( x * x + y * y + z * z ) / 2048;
if( m > 2.5 )
Can be simplified (if perfect (spherical) accuracy is required) by algebraically squaring both sides of the equation to:
double m = (x * x + y * y + z * z) / 4194304;
if (m > 6.25)
Or(preferably, but with slightly less accuracy when combining the axes):
double m = ((abs(x) + abs(y) + abs(z)) / 2048); // Manhattan distance(a diamond in 2D, a four sided, double pyramid in 3d)
if (m > 2.5)
The last line on page 6 of the MPU9250 Datasheet:
states:
1MHz SPI serial interface for communicating with all registers
The next line:
20MHz SPI serial interface for reading sensor and interrupt registers
The FIFO register(Page 27 section 4.17 of the datasheet calls the FIFO a register) is not a sensor nor an interrupt register; therefore communications should be limited to 1 MHZ. Any scanning above this rate MAY(and will in the case of out of phase requests) give invalid/inconstant results.
I have not been able to find a reference for the MPU9250 library, or the commands given, to verify this is the case in your code.
Related
I am the novice in the field of distributed-computation and I know the most popular standard is the Message Passing Interface. However, if I only have one server, I can also run my program under the MPI framework as the following demo example.
# include <cmath>
# include <cstdlib>
# include <ctime>
# include <iomanip>
# include <iostream>
# include <mpi.h>
using namespace std;
int main ( int argc, char *argv[] );
double f ( double x );
void timestamp ( );
int main ( int argc, char *argv[] )
{
double end_time;
int i;
int id;
int ierr;
int m;
int p;
double r8_pi = 3.141592653589793238462643;
int process;
double q_global;
double q_local;
int received;
int source;
double start_time;
MPI_Status status;
int tag;
int target;
double x;
double xb[2];
double x_max = 1.0;
double x_min = 0.0;
//
// Establish the MPI environment.
//
ierr = MPI_Init ( &argc, &argv );
if ( ierr != 0 )
{
cout << "\n";
cout << "INTERVALS_MPI - Fatal error!";
cout << " MPI_Init returned ierr = " << ierr << "\n";
exit ( 1 );
}
//
// Determine this processes's rank.
//
ierr = MPI_Comm_rank ( MPI_COMM_WORLD, &id );
//
// Get the number of processes.
//
ierr = MPI_Comm_size ( MPI_COMM_WORLD, &p );
//
// Say hello (once), and shut down right away unless we
// have at least 2 processes available.
//
if ( id == 0 )
{
timestamp ( );
cout << "\n";
cout << "INTERVALS - Master process:\n";
cout << " C++ version\n";
cout << "\n";
cout << " An MPI example program,\n";
cout << " A quadrature over an interval is done by\n";
cout << " assigning subintervals to processes.\n";
cout << "\n";
cout << " The number of processes is " << p << "\n";
start_time = MPI_Wtime ( );
if ( p <= 1 )
{
cout << "\n";
cout << "INTERVALS - Master process:\n";
cout << " Need at least 2 processes!\n";
MPI_Finalize ( );
cout << "\n";
cout << "INTERVALS - Master process:\n";
cout << " Abnormal end of execution.\n";
exit ( 1 );
}
}
cout << "\n";
cout << "Process " << id << ": Active!\n";
//
// Every process could figure out the endpoints of its interval
// on its own. But we want to demonstrate communication. So we
// assume that the assignment of processes to intervals is done
// only by the master process, which then tells each process
// what job it is to do.
//
if ( id == 0 )
{
for ( process = 1; process <= p-1; process++ )
{
xb[0] = ( ( double ) ( p - process ) * x_min
+ ( double ) ( process - 1 ) * x_max )
/ ( double ) ( p - 1 );
xb[1] = ( ( double ) ( p - process - 1 ) * x_min
+ ( double ) ( process ) * x_max )
/ ( double ) ( p - 1 );
target = process;
tag = 1;
ierr = MPI_Send ( xb, 2, MPI_DOUBLE, target, tag, MPI_COMM_WORLD );
}
}
else
{
source = 0;
tag = 1;
ierr = MPI_Recv ( xb, 2, MPI_DOUBLE, source, tag, MPI_COMM_WORLD, &status );
}
//
// Wait here until everyone has gotten their assignment.
//
ierr = MPI_Barrier ( MPI_COMM_WORLD );
if ( id == 0 )
{
cout << "\n";
cout << "INTERVALS - Master process:\n";
cout << " Subintervals have been assigned.\n";
}
//
// Every process needs to be told the number of points to use.
// Since this is the same value for everybody, we use a broadcast.
// Again, we are doing it in this roundabout way to emphasize that
// the choice for M could really be made at runtime, by processor 0,
// and then sent out to the others.
//
m = 100;
source = 0;
ierr = MPI_Bcast ( &m, 1, MPI_INT, source, MPI_COMM_WORLD );
//
// Now, every process EXCEPT 0 computes its estimate of the
// integral over its subinterval, and sends the result back
// to process 0.
//
if ( id != 0 )
{
q_local = 0.0;
for ( i = 1; i <= m; i++ )
{
x = ( ( double ) ( 2 * m - 2 * i + 1 ) * xb[0]
+ ( double ) ( 2 * i - 1 ) * xb[1] )
/ ( double ) ( 2 * m );
q_local = q_local + f ( x );
}
q_local = q_local * ( xb[1] - xb[0] ) / ( double ) ( m );
target = 0;
tag = 2;
ierr = MPI_Send ( &q_local, 1, MPI_DOUBLE, target, tag, MPI_COMM_WORLD );
}
//
// Process 0 expects to receive N-1 partial results.
//
else
{
received = 0;
q_global = 0.0;
while ( received < p - 1 )
{
source = MPI_ANY_SOURCE;
tag = 2;
ierr = MPI_Recv ( &q_local, 1, MPI_DOUBLE, source, tag, MPI_COMM_WORLD,
&status );
q_global = q_global + q_local;
received = received + 1;
}
}
//
// The master process prints the answer.
//
if ( id == 0 )
{
cout << "\n";
cout << "INTERVALS - Master process:\n";
cout << " Estimate for PI is " << q_global << "\n";
cout << " Error is " << q_global - r8_pi << "\n";
end_time = MPI_Wtime ( );
cout << "\n";
cout << " Elapsed wall clock seconds = "
<< end_time - start_time << "\n";
}
//
// Terminate MPI.
//
MPI_Finalize ( );
//
// Terminate.
//
if ( id == 0 )
{
cout << "\n";
cout << "INTERVALS - Master process:\n";
cout << " Normal end of execution.\n";
cout << "\n";
timestamp ( );
}
return 0;
}
//****************************************************************************80
double f ( double x )
{
double value;
value = 4.0 / ( 1.0 + x * x );
return value;
}
//****************************************************************************80
void timestamp ( )
{
# define TIME_SIZE 40
static char time_buffer[TIME_SIZE];
const struct std::tm *tm_ptr;
std::time_t now;
now = std::time ( NULL );
tm_ptr = std::localtime ( &now );
std::strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm_ptr );
std::cout << time_buffer << "\n";
return;
# undef TIME_SIZE
}
Actually, this is the simple case that we use the MPI to compute the integral of specific function. I run this program by using 4 processes. I am confused that we can also use the OpenMP to do the share memory programming instead of MPI to reduce the communication cost. I do not know the meaning of MPI on single machine.
i'm trying to do a communication over ethernet using ENC28J60 and Arduino Uno and run 3 task . I have a little problem with my arduino code. My code is compiling but i get the following error : "Low memory available, stability problems may occur." and the led on the board is blinking very fast. I guess that the board is trying to alocate memory but he faild. Any idea what can i do ?
#include <Arduino_FreeRTOS.h>
#include "HX711.h"
#include <PID_v1.h>
#include <string.h>
//#include <SPI.h>
#include <UIPEthernet.h>
#define configUSE_IDLE_HOOK 0
// FreeRTOS tasks
void TaskPrimaryControlLoop(void *pvParameters);
void TaskConcentrationControlLoop(void *pvParameters);
void TaskIdle(void *pvParameters);
/* Weigth Cells */
#define hx_pf_dout 3
#define hx_pf_clk 2
#define hx_c_dout 5
#define hx_c_clk 4
HX711 pf_scale(hx_pf_dout, hx_pf_clk);
HX711 c_scale(hx_c_dout, hx_c_clk);
float pf_factor = -236000;
float c_factor = -203000;
float p_weigth = 0; // (kg, 0.000) primary liquid weigth
float p_l_weigth = 0; // (kg, 0.000) primary liquid last weigth
float c_weigth = 0; // (kg, 0.000) concentrate liquid weigth
float c_l_weight = 0; // (kg, 0.000) concentrate liquid last weigth
/* h bridge config */
#define speed_p 9
#define forward_p 7
#define backward_p 8
#define speed_c 6
#define forward_c A0
#define backward_c A1
double p_pump = 0; // 0-255 pwm pump output
double c_pump = 0; // 0-255 pwm pump output
//// PID parameters
// Primary Control Loop
#define p_kp 250.0
#define p_ki 25.0
// Concentration Control Loop
#define c_kp 250.0
#define c_ki 25.0
double p_pv = 0; // (%) primary flow value
double c_pv = 0; // (%) concentration flow value
double p_sp = 0; // (l/min) primary flow setpoint
double c_sp_proc = 0; // % concentration
double c_sp = 0; // (l/min) concentration setpoint
PID pid_pcl(&p_pv, &p_pump, &p_sp, p_kp,p_ki,0.0, DIRECT);
PID pid_ccl(&c_pv, &c_pump, &c_sp, c_kp,c_ki,0.0, DIRECT);
/* Communication Ethernet */
#define MAX_STRING_LEN 32
const byte numChars = 32;
char receivedChars[numChars];
boolean newData = false;
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 2);
IPAddress myDns(192,168,1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
// telnet defaults to port 23
EthernetServer server(23);
//EthernetClient client;
boolean alreadyConnected = false; // whether or not the client was connected previously
void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB, on LEONARDO, MICRO, YUN, and other 32u4 based boards.
}
pinMode(forward_p, OUTPUT);
pinMode(backward_p, OUTPUT);
pinMode(forward_c, OUTPUT);
pinMode(backward_c, OUTPUT);
pinMode(speed_p, OUTPUT);
pinMode(speed_c, OUTPUT);
digitalWrite(backward_p, HIGH);
digitalWrite(backward_c, HIGH);
digitalWrite(forward_p, LOW);
digitalWrite(forward_c, LOW);
pf_scale.set_scale(pf_factor);
c_scale.set_scale(c_factor);
//pf_scale.tare();
//c_scale.tare();
pid_ccl.SetMode(AUTOMATIC);
pid_pcl.SetMode(AUTOMATIC);
xTaskCreate(
TaskPrimaryControlLoop
, (const portCHAR *)"PrimaryControlLoop" // A name just for humans
, 128 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL );
xTaskCreate(
TaskConcentrationControlLoop
, (const portCHAR *)"ConcentrationControlLoop" // A name just for humans
, 128 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 2 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL );
xTaskCreate(
TaskIdle
, (const portCHAR *)"Idle" // A name just for humans
, 512 // This stack size can be checked & adjusted by reading the Stack Highwater
, NULL
, 0 // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
, NULL );
}
void loop() {
}
void TaskPrimaryControlLoop(void *pvParameters)
{
//(void) pvParameters;
for (;;)
{
p_weigth = pf_scale.get_units(1);
p_pv = (p_l_weigth - p_weigth)*100;
if(p_pv < 0) p_pv = 0;
pid_pcl.Compute();
analogWrite(speed_p, p_pump);
p_l_weigth = p_weigth;
vTaskDelay(200 / portTICK_PERIOD_MS); // 200 ms sample time
}
}
void TaskConcentrationControlLoop(void *pvParameters)
{
//(void) pvParameters;
for (;;)
{
c_weigth = c_scale.get_units(1);
c_pv = (c_l_weight - c_weigth)*100;
if(c_pv < 0) c_pv = 0;
c_sp = p_sp * (c_sp_proc/100);
pid_ccl.Compute();
analogWrite(speed_c, c_pump);
c_l_weight = c_weigth;
vTaskDelay(200 / portTICK_PERIOD_MS); // 200 ms sample time
}
}
void TaskIdle(void *pvParameters)
{
//(void) pvParameters;
for(;;){
EthernetClient client = server.available();
// when the client sends the first byte, say hello:
if (client) {
if (!alreadyConnected) {
client.flush();
//Serial.println("We have a new client");
//client.println("Hello, client!");
alreadyConnected = true;
}
}
recvWithStartEndMarkers();
//showNewData();
if(receivedChars[0] == 's'){
p_sp = atof(subStr(receivedChars, ",", 2));
c_sp_proc = int(subStr(receivedChars, ",", 3));
newData = false;
memset(receivedChars, 0, sizeof(receivedChars));
}
// send process values to application
if(receivedChars[0] == 'w'){
Serial.print(p_pv);
Serial.print(",");
Serial.print(p_sp);
Serial.print(",");
Serial.print(int(c_pump));
Serial.print(",");
Serial.print(c_pv);
Serial.print(",");
Serial.print(c_sp);
Serial.print(",");
Serial.print(int(p_pump));
Serial.println();
newData = false;
memset(receivedChars, 0, sizeof(receivedChars));
}
/*
// check commands
while(Serial.available() > 7){
p_sp = Serial.parseFloat();
c_sp_proc = Serial.parseInt();
}
newData = false;
memset(receivedChars, 0, sizeof(receivedChars)
*/
newData = false;
memset(receivedChars, 0, sizeof(receivedChars));
vTaskDelay(1);
}
}
void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;
EthernetClient client = server.available();
while (client.available() > 0 && newData == false) {
rc = client.read();
if (recvInProgress == true) {
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
recvInProgress = false;
ndx = 0;
newData = true;
}
}
else if (rc == startMarker) {
recvInProgress = true;
}
}
}
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// Function to return a substring defined by a delimiter at an index
char* subStr (char* str, char *delim, int index) {
char *act, *sub, *ptr;
static char copy[MAX_STRING_LEN];
int i;
// Since strtok consumes the first arg, make a copy
strcpy(copy, str);
for (i = 1, act = copy; i <= index; i++, act = NULL) {
//Serial.print(".");
sub = strtok_r(act, delim, &ptr);
if (sub == NULL) break;
}
return sub;
}
First thing to do is try to increase the stack depth of your Tasks.
You're currently using 128, 128 and 512.
You can use "StackHighWaterMark" to get the info about the amount of stack space remaining. Try to use this information to "calibrate" the depth of your task. I recommend using at least 40% of free space.
In order to save space in the Arduino memory you can put all the Serial.print in the flash memory, try to use this syntax:
Serial.print(F(","));
Basically you have to add an F in front of the string that you want to print, but you can't do that when you print a variable:
Serial.print(int(c_pump)); // you can't do that here
I am trying to interface an ADNS 9800 mouse chip which I took from "Sharkoon SHARK ZONE M50" . The original PCB is still in place.
I am trying to obtain a framecapture, which should be 30 x 30 pixels. I have connected the ADNS 9800 with SPI to an Arduino UNO Rev 3 (i.e. 5V operating voltage). I.e. MISO, MOSI, SCLK, DGND, AGND, NCS. I did not connect any voltage, since I concluded from previous attempts that that did not yield a good frame capture.
The current problem is that I get a frame capture that is divided in 3 parts: square top left (with a good image of the surroundings), square bottom left (which is a duplicate of top left) and a rectangle on the right half of the screen of monotone grey colour (which does change depending on light conditions). See image. I want the full screen to be one image, not the divided mess it is now. Therefore, it may be a question of the resolution that is used, it may be that it is 15x15 instead of 30x30. However I do not know where this is determined/set.
Also, I find it strange that no input voltage seems to be needed to obtain an image from the camera.
See attachments for frame capture and code (arduino + processing).
Frame output
Arduino code
#include
#include
// Registers
#define REG_Product_ID 0x00
#define REG_Revision_ID 0x01
#define REG_Motion 0x02
#define REG_Delta_X_L 0x03
#define REG_Delta_X_H 0x04
#define REG_Delta_Y_L 0x05
#define REG_Delta_Y_H 0x06
#define REG_SQUAL 0x07
#define REG_Pixel_Sum 0x08
#define REG_Maximum_Pixel 0x09
#define REG_Minimum_Pixel 0x0a
#define REG_Shutter_Lower 0x0b
#define REG_Shutter_Upper 0x0c
#define REG_Frame_Period_Lower 0x0d
#define REG_Frame_Period_Upper 0x0e
#define REG_Configuration_I 0x0f
#define REG_Configuration_II 0x10
#define REG_Frame_Capture 0x12
#define REG_SROM_Enable 0x13
#define REG_Run_Downshift 0x14
#define REG_Rest1_Rate 0x15
#define REG_Rest1_Downshift 0x16
#define REG_Rest2_Rate 0x17
#define REG_Rest2_Downshift 0x18
#define REG_Rest3_Rate 0x19
#define REG_Frame_Period_Max_Bound_Lower 0x1a
#define REG_Frame_Period_Max_Bound_Upper 0x1b
#define REG_Frame_Period_Min_Bound_Lower 0x1c
#define REG_Frame_Period_Min_Bound_Upper 0x1d
#define REG_Shutter_Max_Bound_Lower 0x1e
#define REG_Shutter_Max_Bound_Upper 0x1f
#define REG_LASER_CTRL0 0x20
#define REG_Observation 0x24
#define REG_Data_Out_Lower 0x25
#define REG_Data_Out_Upper 0x26
#define REG_SROM_ID 0x2a
#define REG_Lift_Detection_Thr 0x2e
#define REG_Configuration_V 0x2f
#define REG_Configuration_IV 0x39
#define REG_Power_Up_Reset 0x3a
#define REG_Shutdown 0x3b
#define REG_Inverse_Product_ID 0x3f
#define REG_Snap_Angle 0x42
#define REG_Motion_Burst 0x50
#define REG_SROM_Load_Burst 0x62
#define REG_Pixel_Burst 0x64
byte initComplete=0;
byte testctr=0;
unsigned long currTime;
unsigned long timer;
volatile int xdat;
volatile int ydat;
volatile byte movementflag=0;
const int ncs = 10;
const int lsPin = 4;//ANALOG
const int linearActPin = 9;
extern const unsigned short firmware_length;
extern const unsigned char firmware_data[];
String parseChar = ".";
void setup() {
Serial.begin(115200);
//For first parse put LF and CR there
Serial.println("");
//pinMode(ls, INPUT);
//ADNS 9800 setup
pinMode (ncs, OUTPUT);
SPI.begin();
SPI.setDataMode(SPI_MODE3);
SPI.setBitOrder(MSBFIRST);
//Set clock to 2 MHz
SPI.setClockDivider(8);
performStartup();
dispRegisters();
delay(100);
//Pin modes
pinMode(linearActPin, OUTPUT);
Serial.print("Ready");
Serial.println(parseChar);
//Serial.println("Device is ready");
//FrameCapture();
}
/* DO NOT EDIT BELOW; NECESSARY FOR ADNS9800 */
void performStartup(void){
// reset the chip
adns_com_end(); // ensure that the serial port is reset
adns_com_begin(); // ensure that the serial port is reset
adns_com_end(); // ensure that the serial port is reset
adns_write_reg(REG_Power_Up_Reset, 0x5a); // force reset
delay(50); // wait for it to reboot
// read registers 0x02 to 0x06 (and discard the data)
adns_read_reg(REG_Delta_X_L);
adns_read_reg(REG_Delta_X_H);
adns_read_reg(REG_Delta_Y_L);
adns_read_reg(REG_Delta_Y_H);
// upload the firmware
adns_upload_firmware();
delay(10);
//enable laser(bit 0 = 0b), in normal mode (bits 3,2,1 = 000b)
// reading the actual value of the register is important because the real
// default value is different from what is said in the datasheet, and if you
// change the reserved bytes (like by writing 0x00...) it would not work.
byte laser_ctrl0 = adns_read_reg(REG_LASER_CTRL0);
adns_write_reg(REG_LASER_CTRL0, laser_ctrl0 & 0xf1 );
//0x08 = enable fixed framerate, leave rest standard
//0x10 = disable AGC, leave rest standard
adns_write_reg(REG_Configuration_II, 0x08);
//Set resolution; cpi = REG_value x50
//Min: 0x01 50 cpi
//Max: 0xA4 8200 cpi
adns_write_reg(REG_Configuration_I, 0xA4);
//Set fixed framerate: FR = clk_freq/REG_value = 2000 fps
adns_write_reg(REG_Frame_Period_Max_Bound_Lower, 0xa8);
adns_write_reg(REG_Frame_Period_Max_Bound_Upper, 0x61);
//Set shutter time
adns_write_reg(REG_Shutter_Max_Bound_Lower,0x00);
adns_write_reg(REG_Shutter_Max_Bound_Upper,0x08);
//adns_write_reg(REG_Snap_Angle, 0x80);
delay(1);
Serial.print("Initialized");
Serial.println(parseChar);
}
void adns_com_begin(){
digitalWrite(ncs, LOW);
}
void adns_com_end(){
digitalWrite(ncs, HIGH);
}
byte adns_read_reg(byte reg_addr){
adns_com_begin();
// send adress of the register, with MSBit = 0 to indicate it's a read
SPI.transfer(reg_addr & 0x7f );
delayMicroseconds(100); // tSRAD
// read data
byte data = SPI.transfer(0);
delayMicroseconds(1); // tSCLK-NCS for read operation is 120ns
adns_com_end();
delayMicroseconds(19); // tSRW/tSRR (=20us) minus tSCLK-NCS
return data;
}
void adns_write_reg(byte reg_addr, byte data){
adns_com_begin();
//send adress of the register, with MSBit = 1 to indicate it's a write
SPI.transfer(reg_addr | 0x80 );
//sent data
SPI.transfer(data);
delayMicroseconds(20); // tSCLK-NCS for write operation
adns_com_end();
delayMicroseconds(100); // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound
}
void adns_upload_firmware(){
// send the firmware to the chip, cf p.18 of the datasheet
//Serial.println("Uploading firmware...");
// set the configuration_IV register in 3k firmware mode
adns_write_reg(REG_Configuration_IV, 0x02); // bit 1 = 1 for 3k mode, other bits are reserved
// write 0x1d in SROM_enable reg for initializing
delay(10);
adns_write_reg(REG_SROM_Enable, 0x1d);
// wait for more than one frame period
delay(10); // assume that the frame rate is as low as 100fps... even if it should never be that low
// write 0x18 to SROM_enable to start SROM download
adns_write_reg(REG_SROM_Enable, 0x18);
// write the SROM file (=firmware data)
adns_com_begin();
//write burst destination adress
//bitwise OR to ensure MSB is 1
SPI.transfer(REG_SROM_Load_Burst | 0x80);
delayMicroseconds(50);
// send all bytes of the firmware
unsigned char c;
for(int i = 0; i < firmware_length; i++){
c = (unsigned char)pgm_read_byte(firmware_data + i);
SPI.transfer(c);
delayMicroseconds(15);
}
adns_com_end();
}
void adns_frame_capture(){
//Send signal to start datacollection frame capture
Serial.print("Frame capture");
Serial.println(parseChar);
// reset the chip
adns_write_reg(REG_Power_Up_Reset, 0x5a); // force reset
delay(50); // wait for it to reboot
delay(10);
//Write bytes to Frame_Capture
adns_write_reg(REG_Frame_Capture, 0x93);
adns_write_reg(REG_Frame_Capture, 0xc5);
// wait for more than two frame periods
delay(25); // assume that the frame rate is as low as 100fps... even if it should never be that low
//Check for the first pixel bij reading bit zero of Motion register
//If it is 1, first pixel available
byte motion = adns_read_reg(REG_Motion);
adns_com_begin();
delayMicroseconds(120);//delay t-SRAD = 100 us
byte pixel_burst;
if (motion == 0x21){
//Reading pixel values from ADNS and storing them in Array
for(int i = 0; i < 900; i++){
pixel_burst = adns_read_reg(REG_Pixel_Burst);
//Serial.print(i);
//Serial.print(":");
Serial.print(String(pixel_burst));
Serial.println(parseChar);
delayMicroseconds(15);
}
//Finished transmitting data
Serial.print("Data transfer finished");
Serial.println(parseChar);
//Transfer surface quality value
Serial.print("SQUAL");
Serial.print(String(adns_read_reg(REG_SQUAL)));
Serial.println(parseChar);
}else {
Serial.print("Frame capture failed");
Serial.println(parseChar);
}
adns_com_end();
//Hardware reset and firmware restore required to return navigation
performStartup();
}
void dispRegisters(void){
int oreg[7] = {
0x00,0x3F,0x2A,0x02 };
char* oregname[] = {
"Product_ID","Inverse_Product_ID","SROM_Version","Motion" };
byte regres;
digitalWrite(ncs,LOW);
int rctr=0;
for(rctr=0; rctr<4; rctr++){
SPI.transfer(oreg[rctr]);
delay(1);
//Serial.println("---");
//Serial.println(oregname[rctr]);
//Serial.println(oreg[rctr],HEX);
regres = SPI.transfer(0);
//Serial.println(regres,BIN);
//Serial.println(regres,HEX);
delay(1);
}
digitalWrite(ncs,HIGH);
}
/*********************************************************
DO NOT EDIT ABOVE; NECESSARY FOR RUNNING ADNS9800
*********************************************************/
String data = String();
//Process variables
int run = 0;
int t = 0;
unsigned long t_ms, t_us;
int dt = 0;//1/f = [ms]
long int t_run = 0;//[ms]
unsigned long ms_start, us_start;
void loop() {
if (dt == -1 || t_run == -1){
Serial.print("Time constant error");
Serial.println(parseChar);
}else if (run == 1 && t<t_run){
measure();
Serial.print(data);
Serial.println("");
Serial.println(parseChar);
}else if(run == 1 && t>=t_run){
//Measurement finished
Serial.print("Measurement finished");
Serial.println(parseChar);
digitalWrite(linearActPin, LOW);
run = 0;
t = 0;
}
}
void serialEvent(){
String data_rx;
if (Serial.available() > 0){
//Parse serial data until '.'
data_rx = Serial.readStringUntil('.');
//Remove '.' from buffer
data_rx = data_rx.substring(0, data_rx.length());
//Serial.print(data_rx);
if (data_rx.equals("Run")){
run = 1;
ms_start = millis();
us_start = micros();
digitalWrite(linearActPin, HIGH);
//Read registers and discard data
byte XDataL = adns_read_reg(REG_Delta_X_L);
byte XDataH = adns_read_reg(REG_Delta_X_H);
byte YDataL = adns_read_reg(REG_Delta_Y_L);
byte YDataH = adns_read_reg(REG_Delta_Y_H);
}else if(data_rx.equals("Frame capture run")){
adns_frame_capture();
}else if(data_rx.equals("SQUAL")){
Serial.println(String(adns_read_reg(REG_SQUAL)));
}else if(data_rx.startsWith("dt")){
dt = data_rx.substring(2,data_rx.length()).toInt();
}else if(data_rx.startsWith("trun")){
t_run = data_rx.substring(4,data_rx.length()).toInt();
}
}
}
void measure(void){
/*READ dx, dy, ls
increment t with dt
return String "t,dx,dy,ls"*/
//Read optic flow from ADNS
byte XDataL = adns_read_reg(REG_Delta_X_L);
byte XDataH = adns_read_reg(REG_Delta_X_H);
byte YDataL = adns_read_reg(REG_Delta_Y_L);
byte YDataH = adns_read_reg(REG_Delta_Y_H);
int ls;
unsigned long us, ms;
xdat = int(XDataH<<8);
ydat = int(YDataH<<8);
xdat |=int(XDataL);
ydat |=int(YDataL);
//int between 0-1023, with 5V/1024 = 0.0049 V/unit
ls = analogRead(lsPin);
//Calculate time elapsed between measurements
ms = millis();
us = micros();
t_ms = ms-ms_start;
t_us = us-us_start;
t = t_ms;
//Convert datatypes to string objects and combine
//us can always be divided by 4, so accurate to a resolution of 4 us
String d1 = String(t_ms);
String d2 = String(t_us);
String d3 = String(xdat);
String d4 = String(ydat);
String d5 = String(ls);
data = d2+","+d3+","+d4+","+d5;
//Increment time
delay(dt);
}
Processing code
/* BEP experiment
Communicates with arduino to conduct experiment
Receives and stores data
/
/ DATA PROTOCOL
data_rx
R start measuring
S do screendump
D device is ready
F measurement finished
/
import processing.serial.;
import controlP5.*;
//Serial COMM
Serial arduino;
String data_rx, data_tx;
String parseChar = ".";
//GUI
ControlP5 cp5;
Textfield txtfldDistance, txtfldSpeed, txtfldTs, txtfldN,
txtfldFl, txtfldBron, txtfldPattern, txtfldTrun;
Button btnRun, btnStop, btnFrame;
//File I/O
PrintWriter writer;
String path;
//Runtime variables
int run = 0;
int createWriter = 0;
int frameCapture = 0;
int frameDisplay = 0;
//Time management
String timestamp;
int ms, ms_start;
final int frameX = 30;
final int frameY = 30;
void setup() {
frameRate(60);
time();
//Create GUI
textSize(20);
size(360,660);
//Create textboxes
cp5 = new ControlP5(this);
txtfldDistance = cp5.addTextfield("Distance[m]:")
.setPosition(30, 30)
.setSize(70, 30)
.setAutoClear(false)
.setText("0.5");
txtfldSpeed = cp5.addTextfield("Speed[rev/s]:")
.setPosition(30, 90)
.setSize(70, 30)
.setAutoClear(false);
txtfldTs = cp5.addTextfield("t_s[ms]")
.setPosition(30, 150)
.setSize(70, 30)
.setAutoClear(false)
.setText("10");
txtfldTrun = cp5.addTextfield("t_run[s]")
.setPosition(30, 210)
.setSize(70, 30)
.setAutoClear(false);
txtfldFl = cp5.addTextfield("f[mm]")
.setPosition(130, 30)
.setSize(70, 30)
.setAutoClear(false)
.setText("14");
txtfldBron = cp5.addTextfield("Bron[Watt]")
.setPosition(130, 90)
.setSize(70, 30)
.setAutoClear(false)
.setText("40");
txtfldPattern = cp5.addTextfield("Pattern[mm]")
.setPosition(130, 150)
.setSize(70, 30)
.setAutoClear(false)
.setText("random");
txtfldN = cp5.addTextfield("n")
.setPosition(130, 210)
.setSize(70, 30)
.setAutoClear(false)
.setText("1");
btnRun = cp5.addButton("Run")
.setPosition(230, 270)
.setSize(50,30)
.lock();
btnStop = cp5.addButton("Stop")
.setPosition(150, 270)
.setSize(50,30)
.lock();
btnFrame = cp5.addButton("Frame_Capture")
.setPosition(30, 270)
.setSize(90,30)
.lock();
//Create Serial COMM object
print(timestamp+"SERIAL PORT: ");
println(Serial.list());
// List all the available serial ports:
//arduino = new Serial(this, Serial.list()[2], 115200);
arduino = new Serial(this, Serial.list()[0], 115200);
arduino.clear();
arduino.bufferUntil('.');
}
void draw() {
time();
Frame_Capture();
display_frame();
if (frameDisplay == 1){
display_frame();
frameDisplay = 0;
println(timestamp+"---------------------");
}
}
int n = 0;
int[] frame_capture_data = new int[900];
void serialEvent(Serial arduino){
if (arduino.available() > 0){
//Parse serial data until '.'
data_rx = arduino.readStringUntil('.');
//Remove CR, LF and '.' from buffer
data_rx = data_rx.substring(2, data_rx.length()-1);
//print(n+":");
//println(data_rx);
if(data_rx.equals("Data transfer finished")){
println(timestamp+"Data transfer finished.");
println(timestamp+"Generating visual.");
frameCapture = 0;
frameDisplay = 1;
n = 0;
//unlock textfields
txtfldSpeed.unlock();
txtfldDistance.unlock();
txtfldTs.unlock();
txtfldBron.unlock();
txtfldPattern.unlock();
txtfldFl.unlock();
txtfldN.unlock();
btnRun.unlock();
btnStop.unlock();
btnFrame.unlock();
}else if(data_rx.equals("Ready")){
println(timestamp+"Device is ready.");
println(timestamp+"---------------------");
//unlock textfields
btnRun.unlock();
btnStop.unlock();
btnFrame.unlock();
}else if(data_rx.equals("Initialized")){
println(timestamp+"Device is initialized.");
}else if(data_rx.equals("Measurement finished")){
println(timestamp+"Measurement completed.");
Stop();
}else if(data_rx.equals("Frame capture")){
println(timestamp+"Frame capture transfer started.");
frameCapture = 1;
}else if(data_rx.equals("Frame capture failed")){
println(timestamp+"Frame capture failed. Try again.");
println(timestamp+"---------------------");
//unlock textfields
txtfldSpeed.unlock();
txtfldDistance.unlock();
txtfldTs.unlock();
txtfldBron.unlock();
txtfldPattern.unlock();
txtfldFl.unlock();
txtfldN.unlock();
btnRun.unlock();
btnStop.unlock();
btnFrame.unlock();
}else if(data_rx.contains("SQUAL")){
print(timestamp+"SQUAL: ");
println(data_rx.substring(5,data_rx.length()));
}else if(data_rx.equals("Time constant error")){
print(timestamp+"TIME CONSTANT ERROR");
}else if(frameCapture == 1 && n < 900){
frame_capture_data[n] = int(data_rx);
n++;
}else if(run == 1){
//print(data_rx);
writer.print(data_rx);
}
}
}
public void Run() {
/* When RUN is pressed program starts to run */
//Read value to determine path
float speed = float(txtfldSpeed.getText());
float distance = float(txtfldDistance.getText());
int t_s = int(txtfldTs.getText());
int bron = int(txtfldBron.getText());
int fl = int(txtfldFl.getText());
String pattern = txtfldPattern.getText();
String date = day()+"-"+month();
int n = int(txtfldN.getText());
// Create CSV data file, showing the results from experiment
if (speed > 0 && distance > 0){
if (createWriter == 0){
//Creating objects for writing to file
path = "data/"+date+"/x="+distance+"/"+"x="+distance+"_v="+speed+
"_ts="+t_s+"_f="+fl+"_bron="+bron+"_pat="+pattern+"_n="+n+".csv";
writer = createWriter(path);
//Runtime variables
createWriter = 1;
run = 1;
ms_start = millis();
//Transmit t_s en t_run
arduino.write("dt"+txtfldTs.getText());
arduino.write(parseChar);
arduino.write("trun"+int(txtfldTrun.getText())*1000);
arduino.write(parseChar);
//Transmit starting char to arduino
arduino.write("Run");
arduino.write(parseChar);
//Header
//writer.println("t_ard_ms,t_ard_us,dx,dy,ls");
//lock textfields
txtfldSpeed.lock();
txtfldDistance.lock();
txtfldTs.lock();
txtfldBron.lock();
txtfldPattern.lock();
txtfldFl.lock();
txtfldN.lock();
btnRun.lock();
btnStop.lock();
btnFrame.lock();
println(timestamp+"PROGRAM INITIATED");
println(timestamp+"File stored at: "+path);
}
//ERROR messages
} else if (speed <= 0 && distance <= 0){
println(timestamp+"ERROR: INVALID SPEED AND DISTANCE");
} else if (speed <= 0){
println(timestamp+"ERROR: INVALID SPEED");
} else if (distance <= 0){
println(timestamp+"ERROR: INVALID DISTANCE ");
} else if(txtfldSpeed.getText().equals("")){
println(timestamp+"ERROR: Enter paramaters.");
}
}
public void Stop() {
/* When STOP is pressed program terminates and writes to file */
if (createWriter == 1){
//Write to file and close stream
writer.flush();
writer.close();
//Runtime variables
run = 0;
createWriter = 0;
//unlock textfields
txtfldSpeed.unlock();
txtfldDistance.unlock();
txtfldTs.unlock();
txtfldBron.unlock();
txtfldPattern.unlock();
txtfldFl.unlock();
txtfldN.unlock();
btnRun.unlock();
btnStop.unlock();
btnFrame.unlock();
txtfldN.setText(str(int(txtfldN.getText())+1));
if (int(txtfldN.getText()) > 5){
txtfldN.setText("1");
txtfldSpeed.clear();
}
println(timestamp+"Data written to file.");
println(timestamp+"---------------------");
}
}
public void Frame_Capture() {
arduino.write("Frame capture run");
arduino.write(parseChar);
//lock textfields
txtfldSpeed.lock();
txtfldDistance.lock();
txtfldTs.lock();
txtfldBron.lock();
txtfldPattern.lock();
txtfldFl.lock();
txtfldN.lock();
btnRun.lock();
btnStop.lock();
btnFrame.lock();
}
void display_frame(){
int[] frame1 = new int[225];
int[] frame2 = new int[255];
int x = 30;
int y = 320;
//resolutie 10x10
int s = 10; // size of pixel, i.e. side lengths
//Max res is 30x30
int sz = 10;
int res = 30;
for (int i = 0; i < 15; i++){
for (int m = 0; m < 15; m++){
frame1[15*i+m] = frame_capture_data[30*i+m];
frame2[15*i+m] = frame_capture_data[30*i+m+15];
}
}
//for (int i = 0; i < res*res; i++){
//Commented by Daan:
//for (int j = 0; j < res; j++){ // j resembles the column index.
// for (int k = 0; k < res; k++){ // k resembles the row index
// //fill(map(frame_capture_data[30*j+k],0,63,0,255));
// //frame_capture_data[30*j+k] = 300; // test to see how the pixel values can be manipulated
// fill(float(frame_capture_data[30*j+k]));
// rect(x+j*10, y+300-k*10, s, s);
// //println(frame_capture_data[30*j+k]);
// }
//}
for( int i = 0; i < 900; i++ )
{
fill( map(frame_capture_data[i], 0, 63, 0, 255) ); // Convert from ADNS greyscale to 0 - 255 grey scale format.
rect(x + (i / frameX * sz), // Each rect() is a rectangle that represents a pixel. I.e. width and height of each pixel is "sz".
y +300 - (i % frameY * sz),
sz, sz);
// //rect(off_x + (i % frameX * sz), // Each rect() is a rectangle that represents a pixel. I.e. width and height of each pixel is "sz".
// //off_y + (i / frameY * sz),
// //sz, sz);
}
fill(255,0,0);
rect(x+3*10, y+300-8*10, s, s); // this is red test dot, j = 3 (column), k = 8 (row).
// I.e. this is the 30*3 + 8 = 98 th pixel in frame stream from sensor.
}
public void time(){
/* Keeps track of time
Creates timestamp for messages*/
String h = str(hour());
String m = str(minute());
String s = str(second());
if (int(h) < 10){
h = "0"+h;
} else if(int(m) < 10){
m = "0"+m;
} else if(int(s) < 10){
s = "0"+s;
}
timestamp = "["+h+":"+m+":"+s+"] ";
}
I'm trying to code an LED controller that controls the intensity via PWM. However, my issue is that I can't even get to the loop portion, it seems to hang at when I declare my class. I've tried checking to see if any of my functions in my class are causing the issues, but since I can't even get to loop, there must be something wrong within the class. I've written the class and placed it into a library called LED.
The code is somewhat long, but here it is:
#ifndef LED_H
#define LED_H
#include <LiquidCrystal.h>
#include <Button.h>
#include <EEPROM.h>
#include <TimeLib.h>
#include <PWM.h>
class LED
{
public:
LED();
int read_encoder(); //Reads rotary encoder
void clearLCD();
void setAllLed();
void printLCD();
void setOneLed(int);
int setLed(int, // current time in minutes
int, // pin for this channel of LEDs
int, // start time for this channel of LEDs
int, // photoperiod for this channel of LEDs
int, // fade duration for this channel of LEDs
int, // max value for this channel
bool // true if the channel is inverted
);
void menuWizard();
int subMenuWizard(int, int, bool, bool);
void displayMainMenu();
void printMins(int, bool);
void printHMS(byte,byte,byte);
long EEPROMReadlong(long);
void EEPROMWritelong(int, long);
bool pressSelect();
bool pressBack();
void rotateCheck(int&, int, int);
//variables for the LED channels
int minCounter = 0; // counter that resets at midnight.
int oldMinCounter = 0; // counter that resets at midnight.
int ledPins[5]={2,3,5,6,7};
int ledVal[5]={0,0,0,0,0};
// Variables making use of EEPROM memory:
int variablesList[20];
bool invertedLEDs[5]={false,false,false,false,false};
//Backlight Variables
unsigned long backlightIdleMs = 0;
private:
};
#endif // LED_H
And here is the .cpp file:
#define LCD_RS 35 // RS pin
#define LCD_ENABLE 34 // enable pin
#define LCD_DATA4 33 // d4 pin
#define LCD_DATA5 32 // d5 pin
#define LCD_DATA6 31 // d6 pin
#define LCD_DATA7 30 // d7 pin
#define LCD_BACKLIGHT 9 // backlight pin
// Backlight config
#define BACKLIGHT_DIM 10 // PWM value for backlight at idle
#define BACKLIGHT_ON 70 // PWM value for backlight when on
#define BACKLIGHT_IDLE_MS 10000 // Backlight idle delay
#define ENC_A 14
#define ENC_B 15
#define ENC_PORT PINC
#include <LiquidCrystal.h>
#include <Button.h>
#include <EEPROM.h>
#include <TimeLib.h>
#include <PWM.h>
#include "LED.h"
LiquidCrystal lcd(LCD_RS, LCD_ENABLE, LCD_DATA4, LCD_DATA5, LCD_DATA6, LCD_DATA7);
Button goBack=Button(12, PULLDOWN);
Button select=Button(13, PULLDOWN);
LED::LED()
{
InitTimersSafe();
pinMode(LCD_BACKLIGHT, OUTPUT);
lcd.begin(16, 2);
digitalWrite(LCD_BACKLIGHT, HIGH);
lcd.print("sEx LED, V1");
clearLCD();
delay(5000);
analogWrite(LCD_BACKLIGHT, BACKLIGHT_DIM);
if (variablesList[0] > 1440 || variablesList[0] < 0) {
variablesList[0] = 720; // minute to start this channel.
variablesList[1] = 400; // photoperiod in minutes for this channel.
variablesList[2] = 100; // max intensity for this channel, as a percentage
variablesList[3] = 100; // duration of the fade on and off for sunrise and sunset for
// this channel.
variablesList[4] = 720;
variablesList[5] = 400;
variablesList[6] = 100;
variablesList[7] = 100;
variablesList[8] = 720;
variablesList[9] = 400;
variablesList[10] = 100;
variablesList[11] = 100;
variablesList[12] = 720;
variablesList[13] = 400;
variablesList[14] = 100;
variablesList[15] = 100;
variablesList[16] = 720;
variablesList[17] = 400;
variablesList[18] = 100;
variablesList[19] = 100;
}
else {
variablesList[0] = EEPROMReadlong(0); // minute to start this channel.
variablesList[1] = EEPROMReadlong(4); // photoperiod in minutes for this channel.
variablesList[2] = EEPROMReadlong(8); // max intensity for this channel, as a percentage
variablesList[3] = EEPROMReadlong(12); // duration of the fade on and off for sunrise and sunset for
// this channel.
variablesList[4] = EEPROMReadlong(16);
variablesList[5] = EEPROMReadlong(20);
variablesList[6] = EEPROMReadlong(24);
variablesList[7] = EEPROMReadlong(28);
variablesList[8] = EEPROMReadlong(32);
variablesList[9] = EEPROMReadlong(36);
variablesList[10] = EEPROMReadlong(40);
variablesList[11] = EEPROMReadlong(44);
variablesList[12] = EEPROMReadlong(48);
variablesList[13] = EEPROMReadlong(52);
variablesList[14] = EEPROMReadlong(56);
variablesList[15] = EEPROMReadlong(60);
variablesList[16] = EEPROMReadlong(64);
variablesList[17] = EEPROMReadlong(68);
variablesList[18] = EEPROMReadlong(72);
variablesList[19] = EEPROMReadlong(76);
}
}
void LED::printLCD(){lcd.print("test");clearLCD();delay(2000);lcd.print("testing");clearLCD();}
bool LED::pressSelect(){
if (select.uniquePress()){return 1;}
else {return 0;}
}
bool LED::pressBack(){
if (goBack.uniquePress()){return 1;}
else {return 0;}
}
void LED::clearLCD(){
lcd.clear();
}
void LED::displayMainMenu(){
oldMinCounter = minCounter;
minCounter = hour() * 60 + minute();
for (int i=0;i<17;i=i+4){
if (variablesList[i+3] > variablesList[i+1] / 2 && variablesList[i+1] > 0) {
variablesList[i+3] = variablesList[i+1] / 2;
}
if (variablesList[i+3] < 1) {
variablesList[i+3] = 1;
}
}
//check & set any time functions
if (minCounter > oldMinCounter) {
lcd.clear();
}
lcd.setCursor(0, 0);
printHMS(hour(), minute(), second());
lcd.setCursor(0, 1);
lcd.print(ledVal[0]);
lcd.setCursor(4, 1);
lcd.print(ledVal[1]);
lcd.setCursor(8, 1);
lcd.print(ledVal[2]);
}
int LED::read_encoder()
{
static int enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
static int old_AB = 0;
/**/
old_AB <<= 2; //remember previous state
old_AB |= ( ENC_PORT & 0x03 ); //add current state
return ( enc_states[( old_AB & 0x0f )]);
}
int LED::setLed(int mins, // current time in minutes
int ledPin, // pin for this channel of LEDs
int start, // start time for this channel of LEDs
int period, // photoperiod for this channel of LEDs
int fade, // fade duration for this channel of LEDs
int ledMax, // max value for this channel
bool inverted // true if the channel is inverted
) {
int val = 0;
//fade up
if (mins > start || mins <= start + fade) {
val = map(mins - start, 0, fade, 0, ledMax);
}
//fade down
if (mins > start + period - fade && mins <= start + period) {
val = map(mins - (start + period - fade), 0, fade, ledMax, 0);
}
//off or post-midnight run.
if (mins <= start || mins > start + period) {
if ((start + period) % 1440 < start && (start + period) % 1440 > mins )
{
val = map((start + period - mins) % 1440, 0, fade, 0, ledMax);
}
else
val = 0;
}
if (val > ledMax) {
val = ledMax;
}
if (val < 0) {
val = 0;
}
if (inverted) {
pwmWrite(ledPin, map(val, 0, 100, 255, 0));
}
else {
pwmWrite(ledPin, map(val, 0, 100, 0, 255));
}
return val;
}
void LED::printMins(int mins, //time in minutes to print
bool ampm //print am/pm?
) {
int hr = (mins % 1440) / 60;
int mn = mins % 60;
if (hr < 10) {
lcd.print(" ");
}
lcd.print(hr);
lcd.print(":");
if (mn < 10) {
lcd.print("0");
}
lcd.print(mn);
}
void LED::printHMS (byte hr,
byte mn,
byte sec //time to print
)
{
if (hr < 10) {
lcd.print(" ");
}
lcd.print(hr, DEC);
lcd.print(":");
if (mn < 10) {
lcd.print("0");
}
lcd.print(mn, DEC);
lcd.print(":");
if (sec < 10) {
lcd.print("0");
}
lcd.print(sec, DEC);
}
//EEPROM write functions
long LED::EEPROMReadlong(long address)
{
//Read the 4 bytes from the eeprom memory.
long four = EEPROM.read(address);
long three = EEPROM.read(address + 1);
long two = EEPROM.read(address + 2);
long one = EEPROM.read(address + 3);
//Return the recomposed long by using bitshift.
return ((four << 0) & 0xFF) + ((three << 8) & 0xFFFF) + ((two << 16) & 0xFFFFFF) + ((one << 24) & 0xFFFFFFFF);
}
void LED::EEPROMWritelong(int address, long value)
{
//Decomposition from a long to 4 bytes by using bitshift.
//One = Most significant -> Four = Least significant byte
byte four = (value & 0xFF);
byte three = ((value >> 8) & 0xFF);
byte two = ((value >> 16) & 0xFF);
byte one = ((value >> 24) & 0xFF);
//Write the 4 bytes into the eeprom memory.
EEPROM.write(address, four);
EEPROM.write(address + 1, three);
EEPROM.write(address + 2, two);
EEPROM.write(address + 3, one);
}
void LED::setAllLed(){
int j=0;
for (int i=0;i<17;i=i+4){
int a=i;int b=i+1;int c=i+2;int d=i+3;
ledVal[j] = setLed(minCounter, ledPins[j], variablesList[a], variablesList[b], variablesList[c], variablesList[d], invertedLEDs[j]);
j++;
}
}
void LED::setOneLed(int channel){
int j=channel;
int i=0;
if(channel==1){i+=4;}
if(channel==2){i+=8;}
if(channel==3){i+=12;}
if(channel==4){i+=16;}
int a=i;int b=i+1;int c=i+2;int d=i+3;
ledVal[j] = setLed(minCounter, ledPins[j], variablesList[a], variablesList[b], variablesList[c], variablesList[d], invertedLEDs[j]);
}
void LED::rotateCheck(int& menuCount, int minMenu, int maxMenu){
while (menuCount!=0){
int rotateCount;
rotateCount=read_encoder();
if (rotateCount) {
menuCount+=rotateCount;
if (menuCount<minMenu){menuCount==maxMenu;}
if (menuCount>maxMenu){menuCount==minMenu;}
clearLCD();
}
}
}
void LED::menuWizard(){
int menuCount=1;
String menuList[6]={"Time","LED Max","LED Start","LED End","Fade Length","Ch Override"};
String channelList[5]={"1","2","3","4","5"};
while (menuCount!=0){
rotateCheck(menuCount,1,6);
lcd.setCursor(0, 0);
lcd.print(menuList[menuCount-1]);
clearLCD();
if (goBack.isPressed()){
menuCount=0;
}
if (pressSelect() && menuCount!=0){
int timeMode=1;
int channelCount=0;
bool goBack=0;
while (goBack!=1){
if (menuCount==1){
if (pressSelect()){
timeMode++;
if (timeMode>2){timeMode=1;}
}
int timeAdjDetect=read_encoder();
if (timeMode==1){
if (timeAdjDetect){
if (timeAdjDetect>0){adjustTime(SECS_PER_HOUR);}
if (timeAdjDetect<0) {adjustTime(-SECS_PER_HOUR);}
}
lcd.setCursor(0, 0);
lcd.print("Set Time: Hrs");
lcd.setCursor(0, 1);
printHMS(hour(), minute(), second());
}
else{
if (timeAdjDetect){
if (timeAdjDetect>0){adjustTime(SECS_PER_MIN);}
if (timeAdjDetect<0) {adjustTime(-SECS_PER_MIN);}
}
lcd.setCursor(0, 0);
lcd.print("Set Time: Mins");
lcd.setCursor(0, 1);
printHMS(hour(), minute(), second());
}
clearLCD();
}
else{
rotateCheck(channelCount,0,4);
lcd.setCursor(0,0);
lcd.print("Select Channel");
lcd.setCursor(0,1);
lcd.print(channelList[channelCount]);
clearLCD();
if (pressSelect()){
if (menuCount==2){
subMenuWizard(2,channelCount,0,0);
}
if (menuCount==3){
subMenuWizard(0,channelCount,1,0);
}
if (menuCount==4){
subMenuWizard(1,channelCount,1,1);
}
if (menuCount==5){
subMenuWizard(3,channelCount,1,0);
}
}
}
if (pressBack()){goBack=1;}
}
}
}
for (int i=0;i<20;i++){
int j=0;
EEPROMWritelong(j, variablesList[i]);
j+=4;
}
}
int LED::subMenuWizard(int i, int channel, bool time, bool truetime){
if (channel==1){i=i+4;}
if (channel==2){i=i+8;}
if (channel==3){i=i+12;}
if (channel==4){i=i+16;}
while (!pressBack()){
if (time==0){
rotateCheck(variablesList[i],0,100);
lcd.setCursor(0,0);
lcd.print("Set:");
lcd.setCursor(0,1);
lcd.print(variablesList[i]);
setOneLed(channel);
clearLCD();
}
else{
if (truetime){
rotateCheck(variablesList[i],0,1439);
lcd.setCursor(0,0);
lcd.print("Set:");
lcd.setCursor(0,1);
printMins(variablesList[i] + variablesList[i-1], true);
clearLCD();
}
else {
rotateCheck(variablesList[i],0,1439);
lcd.setCursor(0,0);
lcd.print("Set:");
lcd.setCursor(0,1);
printMins(variablesList[i], true);
clearLCD();
}
setOneLed(channel);
}
}
}
and finally, the .ino file:
#define LCD_BACKLIGHT 9 // backlight pin
#define BACKLIGHT_DIM 10 // PWM value for backlight at idle
#define BACKLIGHT_ON 70 // PWM value for backlight when on
#define BACKLIGHT_IDLE_MS 10000 // Backlight idle delay
#include <LED.h>
//Initialize buttons
int buttonCount = 1;
LED main;
void setup() {
};
void loop() {
/* main.setAllLed();
//turn the backlight off and reset the menu if the idle time has elapsed
if (main.backlightIdleMs + BACKLIGHT_IDLE_MS < millis() && main.backlightIdleMs > 0 ) {
analogWrite(LCD_BACKLIGHT, BACKLIGHT_DIM);
main.clearLCD();
main.backlightIdleMs = 0;
}
if (buttonCount == 1) {
main.displayMainMenu();
}
if (buttonCount == 2) {
main.menuWizard();
buttonCount = 1;
}
*/
main.printLCD();
};
Also, in the loop portion, I've commented the part of code that is intended to run, and I'm running a function that tests to see if I've successfully entered the loop by printing "test" on screen.
I'm using a Mega for this.
LED::LED()
{
InitTimersSafe();
pinMode(LCD_BACKLIGHT, OUTPUT);
lcd.begin(16, 2);
digitalWrite(LCD_BACKLIGHT, HIGH);
lcd.print("sEx LED, V1");
clearLCD();
delay(5000);
analogWrite(LCD_BACKLIGHT, BACKLIGHT_DIM);
You have to understand that this constructor is running when the object is created and that is probably before init() is run from main. So the hardware isn't ready at that point and pinMode and digitalWrite and stuff isn't going to work. The lcd code can't really work there and I bet that is the part that is hanging things.
A constructor should only do things like initialize variables. Any code that relies on the hardware should go into a begin() or init() or whatever method that you can call from setup once it is safe to do those things. The Serial object is a great example of another class that has to do this.
I am trying to control a few (8 for now) servo motors using this 16-channel board. I am running to some issues about accuracy, for example, when moving a couple of motors do draw a diagonal line, because of the delay between each servo, each motor will move in different timing resulting in incorrect drawings.
I am not sure about how to drive the motors in the fastest way in therms of code.
Where to set delays, the baud rate settings for this application, etc. I couldn't find a good example using all channels with minimum delay. In my case, messages are coming from serial, as explained in the code comment.
Is this the right way to drive this board channels?
I am using an arduino uno, but I would like to check if using a Teensy 3.2 results in best performances for this application.
Thanks in advance for any suggestions.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
//#define SERVOMIN 150
//#define SERVOMAX 600
// temporary setting pins for 4 lights - it will be controlled by some decade counter...
//#define L1 4
//#define L2 7
//#define L3 8
//#define L4 10
#define L1 9
#define L2 10
#define L3 11
#define L4 12
/*
* a "pointer" device includes a light and 2 servos. Parameters from serial are:
* index,light,servo1,servo2; <- parameters separated by ',' end of pointer is ';'
*
* example of how serial is coming containing instructions for 4 pointers;
0,0,180,180;1,0,0,0;2,0,180,180;3,0,0,0;
0,0,90,90;1,0,90,90;2,0,90,90;3,0,90,90;
**most of the time these instructions doesn't come all for 4 pointers.
ex:
1,0,12,12;4,255,100,100;
**sometimes it comes only id and light parameter.
0,255;1,0;
(instructions only to turn light on/off)
*/
//values for 8 servos:
const uint8_t SERVOMIN[] = {150, 130, 150, 130, 150, 130, 150, 130};
const uint8_t SERVOMAX[] = {600, 500, 600, 500, 600, 500, 600, 500};
//boards (for now, only one board = 16 servos)
Adafruit_PWMServoDriver pwm [] = {
Adafruit_PWMServoDriver(0x40)
};
uint8_t servonum = 0;
uint8_t activeServos = 4; //not being used now
char buf [4]; //maybe too long
uint16_t currentPointer [4]; //index//light//servo1//servo2
byte lightPin [4] = {L1, L2, L3, L4};
uint8_t lightstatus [4] = {0, 0, 0, 0};
//debug
String inputString = ""; // a string to hold incoming data
boolean stringComplete = false; // whether the string is complete
boolean feedback = false;
void setup() {
//temporally as digital outputs
pinMode(L1, OUTPUT);
pinMode(L2, OUTPUT);
pinMode(L3, OUTPUT);
pinMode(L4, OUTPUT);
Serial.begin(115200);//230400 //115200 //57600 //38400 ?
for ( uint8_t i = 0; i < sizeof(pwm); i++) {
pwm[i].begin();
pwm[i].setPWMFreq(60);
}
}
void loop() {
reply();
}
void reply() {
if (stringComplete) {
if (feedback) Serial.println(inputString);
// clear the string:
inputString = "";
stringComplete = false;
for ( int i = 0; i < sizeof(buf); ++i ) buf[i] = (char)0;
}
}
void serialEvent() {
static byte ndx = 0;
static int s = 0;
while (Serial.available()) {
char rc = (char)Serial.read();
inputString += rc;
//(2) setting pointer parameter
if ( rc == ',') {
setPointer(s);
s++;
for ( int i = 0; i < sizeof(buf); ++i ) buf[i] = (char)0;
ndx = 0;
}
//(3) end of this pointer instruction
else if (rc == ';') {
setPointer(s);
//executePointer(); //plan B
ndx = 0;
s = 0;
for ( int i = 0; i < sizeof(buf); ++i ) buf[i] = (char)0;
}
//(4) end of command line
else if (rc == '\n') {
//p = 0;
s = 0;
stringComplete = true;
}
//(1) buffering
else {
buf[ndx] = rc;
ndx++;
}
}
}
void setPointer(int s) {
//index//light//servo1//servo2
int value;
value = atoi(buf);
//index
if (s == 0) {
if (feedback) {
Serial.print("index:");
Serial.print(value);
Serial.print(", buf:");
Serial.println(buf);
}
currentPointer[0] = value;
}
//light
else if (s == 1) {
int index = currentPointer[0];
currentPointer[s] = value;
//Serial.println(index);
digitalWrite(lightPin[index], (value > 0) ? HIGH : LOW);
// analogWrite( lightPin[currentPointer[0]], currentPointer[1]); // implement later
if (feedback) {
Serial.print("light: ");
Serial.println(value);
}
//servos
} else {
int index = currentPointer[0];
if (feedback) {
Serial.print("servo ");
Serial.print(index * 2 + s - 2);
Serial.print(": ");
Serial.println(value);
}
uint16_t pulselen = map(value, 0, 180, SERVOMIN[index], SERVOMAX[index]);
currentPointer[s] = pulselen;
pwm[0].setPWM(index * 2 + (s - 2), 0, pulselen); //current pointer id * 2 + s (s is 2 or 3)
//delay(20);
}
}
// this was plan B - not using
void executePointer() {
int index = currentPointer[0];
analogWrite( lightPin[index], currentPointer[1]);
pwm[0].setPWM(index * 2, 0, currentPointer[2]);
pwm[0].setPWM(index * 2 + 1, 0, currentPointer[3]);
delay(20);
}