Realtime sending PWM values using 433Mhz transmitter - c++

I have tried to create wireless PWM transmission using 433Mhz transmitter modules. I found this library for transmiting https://github.com/zeitgeist87/RFTransmitter and this library for reading PWM values on some pin https://github.com/xkam1x/Arduino-PWM-Reader.
I wrote code for sending PWM values:
#include "PWM.hpp"
#include <RFTransmitter.h>
#define NODE_ID 1
#define OUTPUT_PIN 11
RFTransmitter transmitter(OUTPUT_PIN, NODE_ID);
PWM my_pwm(2);
// the setup function runs once when you press reset or power the board
void setup() {
my_pwm.begin(true);
}
// the loop function runs over and over again forever
void loop() {
int pwmValue = my_pwm.getValue();
char stringValue[4];
itoa(pwmValue, stringValue, 10);
transmitter.send(stringValue, strlen(stringValue) + 1);
}
And similar code for receiving
#include <Servo.h>
#include <PinChangeInterruptHandler.h>
#include <RFReceiver.h>
int PWM_out_pin = 9;
Servo servo;
// Listen on digital pin 2
RFReceiver receiver(2);
void setup() {
servo.attach(PWM_out_pin);
receiver.begin();
}
void loop() {
char msg[MAX_PACKAGE_SIZE];
byte senderId = 0;
byte packageId = 0;
byte len = receiver.recvPackage((byte *)msg, &senderId, &packageId);
String *stringObject = new String(msg);
servo.writeMicroseconds(stringObject->toInt());
}
It works, but it has few problems.
First is that is not optimal. I transforming all to string. How can I send int values from PWM directly?
Second problem is that it has about 1 sec delay. Does it possible make it faster? I need it for realtime controlling servo.
Thanks.

How can I send int values from PWM directly?
transmitter.send((char*)pwmValue, sizeof(int));
Then on the receive side, you don't need to convert the data to a string.
servo.writeMicroseconds((int) *msg);
(int) and (char*) are C-style typecasts. Essentially telling the compiler that you want the data to be interpreted as a different variable type.
WARNING: Be careful with the "new" keyword, you are dynamically allocating memory. Rule of thumb in C/C++: Wherever you use the "new" keyword there should be a corresponding "delete" call that cleans up the memory.
In terms of making it faster, your C/C++ code here is not the limiting factor causing the 1 sec latency from command to action. Most likely there is a parameter in your RF transmitter transmit/receive stack that needs to be tweaked. Before sending PWM values, I would benchmark your latency of sending and printing out a simple string.

Related

Serial Read - Arduino - DELAY

I am a new programmer, so I am having a bit of problem with Serial communication of Arduino.
I am trying to read data from serial Input, sent by a simulation as characters and I need to store it as integer to write it with my servo.
I found this https://forum.arduino.cc/t/serial-input-basics-updated/382007 tutorial and example 4 does the job,
However, the simulation sends the data so fast that Arduino bottlenecks and the data pile up in the serial port and even if I stop the simulation the Arduino continues to perform the messages.
How can I slow down the data receiving like read data every 0.3 seconds instead. I tried to put some delays but it seems like it doesn't work.
Also, how can I change the code in a way that it stops performing new thing when there is no new serial messages and cancel the ones in the queue?
const byte numChars = 32;
char receivedChars[numChars]; // an array to store the received data
boolean newData = false;
//SERVO//
#include <Servo.h>
Servo myservo; // create servo object to control a servo
////////////////////////
int dataNumber = 0; // new for this version
void setup() {
Serial.begin(9600);
pinMode(LED_BUILTIN, OUTPUT);
myservo.attach(9); // attaches the servo on pin 9 to the servo object
Serial.println("<Arduino is ready>");
}
void loop() {
recvWithEndMarker();
showNewNumber();
}
void recvWithEndMarker() {
static byte ndx = 0;
char endMarker = '\n';
char rc;
if (Serial.available()> 0) {
rc = Serial.read();
if (rc != endMarker) {
receivedChars[ndx] = rc;
ndx++;
if (ndx >= numChars) {
ndx = numChars - 1;
}
}
else {
receivedChars[ndx] = '\0'; // terminate the string
ndx = 0;
newData = true;
delay(1);
}
}
}
void showNewNumber() {
if (newData == true) {
dataNumber = 0; // new for this version
dataNumber = atoi(receivedChars); // new for this version
Serial.print("This just in ... ");
Serial.println(receivedChars);
Serial.print("Data as Number ... "); // new for this version
Serial.println(dataNumber); // new for this version
myservo.write(dataNumber); // sets the servo position according to the scaled value
delay(50);
newData = false;
}
}
Thanks!
Welcome to the forum.
I'll admit that I don't know about your arduino set-up, but I hope that I can help.
Serial ports are asynchronous sources of data.
For base 3 wire RS-232, the receiver can't control the speed at which data is received other than baud rate hence received data is copied into a buffer (array) before it is processed.
This is to give your code time to process the received data before more messages arrive and cause what is known as a buffer overrun, corrupting data already received. Think of the serial link as being a hose pipe filling a bucket (the buffer) with water and you emptying it using a cup (your processing).
If your processing code is running too slowly and you are losing data then one option is to increase the size of the reception buffer, say from 32 to 255.
Adding delays to the reception code will only make matters worse.
An important point to make is that you must ensure that any processed data is removed from the buffer otherwise it will be processed again.
If your processing is fast enough then a nasty method is to just clear the buffer of all data by setting all array values to 0.
Another method is to use is keep a records (index value) of the next available location to write to and read from.
Data from the serial port is written into the buffer address using the write index value saved previously as a starting point. It is updated to take into account the size of the data written and incremented to indicate where to start the next write operation.
Your processing reads from the buffer using the last read index until it detects your end of message indicator and increments the read index to indicate the next location to read from.
It may be that your arduino serial port supports hardware flow control raising a Ready To Receive line when the hardware buffer (in the serial port itself) is full. This would be set before you open it.
Code:
Remove Delay calls - they only slow down your code.
Sending data out Serial.print, Serial.println commands take time,
place those after myservo.write
Remove Serial.print type commands that aren't strictly necessary.

Adding more data in PROGMEM breaks SPI transfer on Arduino Mega 2560

I'm working on a small project (or so I thought) involving an Arduino Mega (2560) and a Waveshare ePaper display.
I've got it working alright with the library (epd7in5) and I've added two images into PROGMEM. But as soon as I add a third image (and thus a third entry into the PROGMEM), somehow the ePaper screen doesn't initiate anymore. Adding some debugging in the library shows that the code gets stuck on a specific SPI.transfer().
EDIT: Theory
Is it possible that SPI is not compatible when there's too much data in flash? I've read up on it that 64kb is the max. I'm slightly above that with two images, but significantly so with three. Could be that this breaks SPI? And if so: can I fix it?
I've added the code below and the specific part of the library where the SPI.transfer() fails.
Main.cpp
Removing the code related to dummy3 ensures that the dummy3 array doesn't compile. Only using dummy1 and dummy2 everything works fine. Adding dummy3 and the program gets stuck on epd.Init().
#include <SPI.h>
#include <epd7in5.h>
#include "imagedata.h"
Epd epd;
void debug(String);
void setup() {
Serial.begin(9600);
debug("Serial begin");
if (epd.Init() != 0) {
debug("INIT FAILED!");
return;
}
debug("Changing image");
epd.DisplayFrame(dummy1); //DisplayFrame by default includes WaitUntilIdle.
debug("dummy1 on ePaper");
delay(1000);
debug("Changing image");
epd.DisplayFrame(dummy2);
debug("dummy2 on ePaper");
delay(1000);
debug("Changing image");
epd.DisplayFrame(dummy3);
debug("dummy2 on ePaper");
epd.SendCommand(POWER_OFF);
debug("POWER_OFF");
}
void loop() {
}
void debug(String message) {
Serial.print(millis());
Serial.print("\t");
Serial.println(message);
}
imagedata.cpp
I've removed the actual image data as it's A LOT.
Two images result in a total flash of 67326 bytes (about 26% of total flash mem of 2560).
Three images result in a total flash of 98052 bytes (about 38% of total flash mem of 2560).
Headerfile contains simply the declarations.
#include "imagedata.h"
#include <avr/pgmspace.h>
const unsigned char dummy1[30726] PROGMEM = {...data...};
const unsigned char dummy2[30726] PROGMEM = {...data...};
const unsigned char dummy3[30726] PROGMEM = {...data...};
epd7in5.cpp
I've added the debug function. SendData is also included and uses the debug as well.
void Epd::debug(String message) {
Serial.print(millis());
Serial.print("\t");
Serial.print("EPD");
Serial.print("\t");
Serial.println(message);
}
int Epd::Init(void) {
if (IfInit() != 0) {
return -1;
}
debug("Resetting");
Reset();
debug("SendCommand(POWER_SETTING);");
SendCommand(POWER_SETTING);
debug("SendData(0x37);");
SendData(0x37);
debug("SendData(0x00);");
SendData(0x00);
debug("SendCommand(PANEL_SETTING);");
SendCommand(PANEL_SETTING);
SendData(0xCF);
SendData(0x08);
SendCommand(BOOSTER_SOFT_START);
SendData(0xc7);
SendData(0xcc);
SendData(0x28);
SendCommand(POWER_ON);
WaitUntilIdle();
SendCommand(PLL_CONTROL);
SendData(0x3c);
SendCommand(TEMPERATURE_CALIBRATION);
SendData(0x00);
SendCommand(VCOM_AND_DATA_INTERVAL_SETTING);
SendData(0x77);
SendCommand(TCON_SETTING);
SendData(0x22);
SendCommand(TCON_RESOLUTION);
SendData(0x02); //source 640
SendData(0x80);
SendData(0x01); //gate 384
SendData(0x80);
SendCommand(VCM_DC_SETTING);
SendData(0x1E); //decide by LUT file
SendCommand(0xe5); //FLASH MODE
SendData(0x03);
return 0;
}
void Epd::SendData(unsigned char data) {
debug("DigitalWrite(dc_pin, HIGH);");
DigitalWrite(dc_pin, HIGH);
debug("SpiTransfer(data);");
SpiTransfer(data);
}
epdif.cpp
This is the SPI transfer part that doesn't continue.
void EpdIf::SpiTransfer(unsigned char data) {
digitalWrite(CS_PIN, LOW);
SPI.transfer(data);
digitalWrite(CS_PIN, HIGH);
}
The serial print of the project is as follows...
0 Serial begin
0 EPD Resetting
400 EPD SendCommand(POWER_SETTING);
400 EPD SendData(0x37);
400 EPD DigitalWrite(dc_pin, HIGH);
435 EPD SpiTransfer(data);
So, as soon as it runs SpiTransfer the code simply stops working. It seems it is in an infinite loop within SPI.transfer(); but I don't know exactly how that would happen. I don't see how PROGMEM could interfere with the transfer and I have enough flash memory left...
What can be solution for this? Is it a problem in SPI that I need to change? Or do I need to store my data differently in PROGMEM? I am a bit at a loss.
Thanks in advance for your help, greatly appreciated.
Your problem is not with SPI (itself).
It is related to the amount of data you have in progmem and reading it. If you are using more than 64k of Progmem you run into 2 different sets of problems:
Reading the data in a reliable way
Reading the progmem should use the pgm_read_xxx_far macros for any
address after 65536. Epd::DisplayFrame uses
pgm_read_byte(&frame_buffer[i]); so you have a problem on the
library level. I'm not familiar with this library, so I'm not sure
if there's an alternative function that you can call, and provide
the buffer yourself, after reading it from PROGMEM with
pgm_read_byte_far. This is the easy part of the problem
Getting said data into PROGMEM in the first place
The Arduino core, and the avr compiler themselves assume that all pointers are only 16 bits. Putting data into PROGMEM makes it appear before the arduino core and executable code in the final executable that runs your program.
The reliable way to get this going correctly is with a custom linker script, that will get your data out of the way and place it after all executable code. This would be hard, and I'm afraid I can't give any info on how to accomplish that.
Alternatively, you can try to use _attribute__((section(".fini2"))) to use the .fini2 section. Some people have done it before, and it has worked for them.
You would use it like that:
const unsigned char __attribute__((section(".fini2"))) dummy1[30726] = {...data...};
The last alternative would be to not use PROGMEM at all, and have some kind of external storage that you use for your data (e.g. some SDCard). This would most probably be the easiest way to tackle that issue.
Source: This excellent thread and the wonderfull insight by westfw on the arduino forums:
https://forum.arduino.cc/index.php?topic=485507.0

how to create an interrupt or return value for servo motor?

It's been a frustrating day of learning from Arduino tuts.
I'm working on setting up a servo motor. I troubleshooted why I couldn't get it to receive data and now I got it reading through the serial port and turning to appropriate degrees. But now it seems every time it loops through it tries to reset it's value to 0 degrees. I haven't assigned the variable to be 0 and the while loop is supposed to act like an interrupt while it waits for user input. So I don't understand why it's doing this.
I've also tried to return the value pos to keep from changing/resetting values during each loop but keep getting compiling errors. I got one saying that pos returns void. And then I got another one when trying to declare the int pos as a method within the loop and nesting the rest of the code inside the int method.
Also interesting side note: when you launch the serial port window in the IDE it'll rotate the motor by a small amount despite no input being given. After an given input is entered, it'll go to those degrees then it resets as described before.
Code:
#include <Servo.h> //Including the Servo code library
int servoPin = 6;
int servoDelay = 25;
int pos;
Servo myPointer; // Create a Servo object called myPointer
void setup() {
Serial.begin(9600);
//pinMode (servoPin, OUTPUT);
myPointer.attach(servoPin);
Serial.println("Hello");
}
void loop() {
Serial.println ("Where would you like the servo to point?");
while (Serial.available()==0){
}
pos = Serial.parseInt();
Serial.println (pos);
myPointer.write(pos);
}
The servo is running off the 5V power supply on the Arduino and receives instructions OK. It does not reset the position when running through void setup() so this must be the loop causing this. I just don't know why or how to fix it.
You have ensure the entire message is received before you start to parse it. Either set a the inter character gap with Serial.setTimeout() (default 1s) to a larger value before use:
Serial.setTimeout(2000);
while (Serial.available() == 0) {}
duration = Serial.parseInt();
Or add a delay between Serial.available() and Serial.parseInt() if the above does not solve all your problems.
while (Serial.available() == 0) {}
delay(4000);
duration = Serial.parseInt();
Serial.available() would fall thru on the first character received. So:
Setting the inter character timeout would extend the time before parsing by waiting after each new character to see if any further characters would be received.
A delay before parsing would ensure that the entire message is received before you try and parse it.
Additional characters like \n or \r could also be present triggering the parse but without there being sensible data. Serial.parseInt() returns 0 if it fails to convert a number. So:
Do a range check for 1 to 360 degrees to catch these without losing much range of turning.
Check if your terminal appends \n or \r.
After parsing empty the receive buffers with Serial.flush() to get rid of any remaining \n or \r characters.

Using 4 16bit timers for 400hz PWM

I'm dealing with arduino mega based quadcopter and trying to make PWM frequency for 4 motors - 400hz each. I've found an interesting solution where 4 ATmega2560 16bit timers are used to control 4 ESCs with PWM so it could reach 400hz frequency. 700 to 2000µs are normal pulse widths ESC are dealing with.
1sec/REFRESH_INTERVAL = 1/0.0025 = 400hz.
this is servo.h lib:
#define MIN_PULSE_WIDTH 700 // the shortest pulse sent to a servo
#define MAX_PULSE_WIDTH 2000 // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH 1000 // default pulse width when servo is attached
#define REFRESH_INTERVAL 2500 // minimum time to refresh servos in microseconds
#define SERVOS_PER_TIMER 1 // the maximum number of servos controlled by one timer
#define MAX_SERVOS (_Nbr_16timers * SERVOS_PER_TIMER)
The problem is to make it work each PWM should be controlled with 1 16bit timer. Otherwize, say, 2 escs on 1 timer would give 200hz. So all of 16bit timers are busy controlling 4 ESC but I still need to read input PPM from receiver. To do so I need at least one more 16bit timer which I don't have anymore. It's still one 8bit timer free bit it can only read 0..255 numbers while normal number escs operate with are 1000..2000 and stuff.
So what would happen if I'll use same 16bit timer for both pwm and ppm reading? Would it work? Would it decrease speed drastically? I have arduino working in pair with Raspberry Pi which controls data filtering, debugging, and stuff, is it better to move ppm reading to Raspberry?
To answer one of your questions:
So what would happen if I'll use same 16bit timer for both pwm and ppm
reading? Would it work?
Yes. When your pin change interrupt fires you may just read the current TCNT value to find out how long it has been since the last one. This will not in any way interfere with the timer's hardware PWM operation.
Would it decrease speed drastically?
No. PWM is done by dedicated hardware, software operations running at the same time will not affect its speed and neither will any ISRs you may have activated for the corresponding timer. Hence, you can let the timer generate the PWM as desired and still use it to a) read the current counter value from it and b) have an output compare and/or overflow ISR hooked to it to create a software-extended timer.
Edit in response to your comment:
Note that the actual value in the TCNT register is the current timer (tick) count at any moment, irrespective of whether PWM is active or not. Also, the Timer OVerflow interrupt (TOV) can be used in any mode. These two properties allow to make a software-extended timer for arbitrary other time measurement tasks via the following steps:
Install and activate a timer overflow interrupt for the timer/counter you want to use. In the ISR you basically just increment a (volatile!) global variable (timer1OvfCount for example), which effectively counts timer overflows and thus extends the actual timer range. The current absolute tick count can then be calculated as timer1OvfCount * topTimerValue + TCNTx.
When an event occurs, e.g a rising edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount and store these values in another global variable (e.g. startTimestamp), effectively starting your time measurement.
When the second event occurs, e.g. a falling edge on one pin, in the handling routine (e.g. pin-change ISR) you read the current timer/couter (TCNT) value and timer1OvfCount. Now you have the timestamp of the start of the signal in startTimestamp and the timestamp of the end of the signal in another variable. The difference between these two timestamps is exactly the duration of the pulse you're after.
Two points to consider though:
When using phase-correct PWM modes the timer will alternate between counting up and down successively. This makes finding the actual number of ticks passed since the last TOV interrupt a little more complicated.
There may be a race condition between one piece of code first reading TCNT and then reading timer1OvfCount, and the TOV ISR. This can be countered by disabling interrupts, then reading TCNT, then reading timer1OvfCount, and then checking the TOV interrupt flag; if the flag is set, there's a pending, un-handled overflow interrupt -> enable interrupts and repeat.
However, I'm pretty sure there are a couple of library functions around to maintain software-extended timer/counters that do all the timer-handling for you.
what is unit of 700 and 2000?I guess usec.You have not exaplained much in your question but i identified that you need pulses of 25msec duration in which 700 usec on time may be 0 degree and 2000 may be for 180 degree now pulse input of each servo may be attached with any GPIO of AVR.and this GPIOs provide PWM signal to Servo.so i guess you can even control this all motors with only one timer.With this kind of code:
suppose you have a timer that genrate inturrupt at every 50 usec.
now if you want 700 usec for motor1,800 usec for motor 2,900 usec for motor 3 & 1000 usec for motor 4 then just do this:
#define CYCLE_PERIOD 500 // for 25 msec = 50 usec * 500
unsigned short motor1=14; // 700usec = 50x14
unsigned short motor2=16; // 800usec
unsigned short motor3=18; // 900usec
unsigned short motor4=20; // 1000usec
unsigned char motor1_high_flag=1;
unsigned char motor2_high_flag=1;
unsigned char motor3_high_flag=1;
unsigned char motor4_high_flag=1;
PA.0 = 1; // IO for motor1
PA.1 = 1; // IO for motor2
PA.2 = 1; // IO for motor3
PA.3 = 1; // IO for motor4
void timer_inturrupt_at_50usec()
{
motor1--;motor2--;motor3--;motor4--;
if(!motor1)
{
if(motor1_high_flag)
{
motor1_high_flag = 0;
PA.0 = 0;
motor1 = CYCLE_PERIOD - motor1;
}
if(!motor1_high_flag)
{
motor1_high_flag = 1;
PA.0 = 1;
motor1 = 14; // this one is dummy;if you want to change duty time update this in main
}
}
if(!motor2)
{
if(motor2_high_flag)
{
motor2_high_flag = 0;
PA.1 = 0;
motor2 = CYCLE_PERIOD - motor2;
}
if(!motor2_high_flag)
{
motor2_high_flag = 1;
PA.1 = 1;
motor2 = 16;
}
}
if(!motor3)
{
if(motor3_high_flag)
{
motor3_high_flag = 0;
PA.2 = 0;
motor3 = CYCLE_PERIOD - motor3;
}
if(!motor3_high_flag)
{
motor3_high_flag = 1;
PA.2 = 1;
motor3 = 18;
}
}
if(!motor4)
{
if(motor4_high_flag)
{
motor4_high_flag = 0;
PA.3 = 0;
motor4 = CYCLE_PERIOD - motor4;
}
if(!motor4_high_flag)
{
motor4_high_flag = 1;
PA.3 = 1;
motor4 = 19;
}
}
}
& tell me what is ESC?

Arduino Serial.available() keeps increasing

I'm using an Arduino Uno R3 and when I send an AT command to my GSM shield via Serial, I get an increasing number from Serial.available().
Here is the example I have been using to debug:
void loop()
{
Serial.println("AT+CADC?");
delay(3000);
}
void serialEvent()
{
char * sensorValue;
int serial = Serial.available();
Serial.print("-");
Serial.print(serial);
Serial.println("-");
if(serial >0)
{
sensorValue = (char*) malloc(sizeof(char) * (serial +1));
int i;
for(i = 0; i < serial; i++)
{
sensorValue[i] = Serial.read();
//Serial.print(sensorValue[i]);
}
sensorValue[serial+1] = '\0';
Serial.print(sensorValue);
}
delay(2000);
}
The result I get from the serial monitor is:
-30-
-63-
-63-
-63-
...
Why does the number of bytes available start off at 30 and then max out at 63? This happens even when I use Serial.read(), which should consume the data in the buffer.
sensorValue[serial+1] = '\0';
Serial.print(sensorValue); // <== here
You send whatever you receive right back to the modem. Which promptly echoes it back. So once you got it going with an AT command, you'll forever loop sending the same bytes back and forth. Remove the Serial.print() calls.
I'd suggest you use the software serial library—SoftwareSerial—and use that to access the GSM modem, unless the GSM modem requires UART or RS-232 level signals.
Another alternative, which might be appropriate if most of your work involves talking to modems, would be an Arduino Mega 2560. It has four hardware serial interfaces, though it doesn't have RS-232 output signals. For that, you should be able to use one of the FTDI breakout boards.