I am making a system where I have a Pi that will send a string over UDP to an Arduino that has Wifi. The Arduino will then send some data back to the Pi. This part works flawlessly. Then the Pi will relay this same data over Serial to an Arduino Mega that has no Wifi. This is my problem. I am able to receive all the data over UDP but when I try to receive data and then send this over Serial, it glitches and nothing works. It just receives null values. I know this because on the Arduino Mega, I have setup a system where it will simply take the data it received and just send it back with the letters "ACK" signed on them so that I know that it was successfully received. All I get is null things. But when I just send the string "HI" over the Serial port and disable the UDP stuff, it magically works... sometimes. What could be going wrong?
Code on the Pi
#!/usr/bin/env python3
import serial
import time
import socket
serialData = 'null \n'
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
message = b'pi'
addr = ("192.168.71.146", 2390)
def wifirecv():
global serialData
s.sendto(message, addr)
data, address = s.recvfrom(10240)
#print(data.decode())
angle = data.decode()
serialData = angle + ' \n'
while(True):
ser = serial.Serial('/dev/ttyACM0', 115200, timeout=1)
ser.flush()
ser.close()
ser.open()
while True:
try:
wifirecv()
print(serialData)
ser.write(serialData.encode('utf-8'))
#ser.write(b'hi')
line = ser.readline().decode('utf-8').rstrip()
print(line)
except(KeyboardInterrupt):
ser.close()
print("Serial Closed")
exit()
Code on the Mega
void setup() {
Serial.begin(115200);
}
void loop() {
while(Serial.available() != 0){
String data = Serial.readStringUntil('\n');
Serial.println(data + ": ACK");
}
}
Despite you mention that the response from the Arduino works fine, I believe that you should use the SerialEvent interrupt callback rather than polling for the .available method. It is a much more reliable way to detect when data is present in the input buffer. I would form the input string char by char, as done in the official tutorial.
I think that the approach you opted for might be causing timing issues on the serial port since you said:
I am able to receive all the data over UDP but when I try to receive data and then send this over Serial, it glitches and nothing works
those glitches might also be a consequence of the selected baudrate or (again) the polling approach. This is reinforced by your statement:
Serial port and disable the UDP stuff, it magically works... sometimes
Have you tried using read_until(LF, None) instead of readline()? I've had better results with the first one in some acquisition routines. Check this for reference
Are you using any other resources from the Pi or the Arduino? I am not sure if the UDP messes up with the serial communication from the RPi. I would seriously doubt it, but just check if there are no reported issues with the socket library.
Finally, a tiny time.sleep(ms) between the write() and the read() in your RPi might not hurt the communication too much. For instance, in my recent experience with the I2C protocol, a tiny delay makes a huge difference for reliable communication.
The bottom line is: you have timing issues.
Related
I'm working on a project based on the ESP32 platform. The aim is to count the number of MAC addresses in the area, and transmit this information over WiFi (using an http POST request).
The first task is achieved by sniffing WIFI packets and collecting the contained addresses, following this example: https://blog.podkalicki.com/esp32-wifi-sniffer/
I believe that the code which "sniffs" the packets sets the ESP to run in promiscuous mode, and therefore I cannot connect to any AP anymore.
I've tried several solutions, first starting with timer interrupts. However this approach always led to a Core Panic and reset of the chip.
I also learnt I could use RTOS to run different tasks in parallel on the two cores of the CPU, but that didn't help to solve the problem.
void wifi_sniffer_packet_handler(void* buff, wifi_promiscuous_pkt_type_t type)
{
if (type != WIFI_PKT_MGMT)//aggiungere filtro su RSSI a questa altezza.
return;
const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff;
const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload;
const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr;
//some analysis and then print the MAC address
}
void setup() {
Serial.begin(115200);
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &chUpdate, true);
timerAlarmWrite(timer, 1000000, true);//timer, arr_val, reload=true
delay(4000);
wifi_sniffer_init();
timerAlarmEnable(timer);
}
// the loop function runs over and over again forever
void loop() {
//Serial.print("inside loop");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Establishing connection to WiFi..");
}
Serial.println("Connected to network");
}
I also noticed that the code in the loop gets stuck into the while, and is restarted every time the packet handler is run (I nevere get to see "Connected to network", but i see "Establishing connection to WiFi.." several times.
Anyone can explain me what's going on? Is there a different approach to achieve this result?
Thank you.
You may have two tasks and two cores, but the ESP32 still has only one wifi chip. The way your code is written (at least, the code you shared), you'll be trying to connect to a wifi network at the same time as you're trying to run promiscuous mode. You can do only one of those things at a time.
You'll need to stop promiscuous mode before you attempt to connect to an access point. Right now your code constantly attempt to connect to a wifi access point. Use a volatile variable to store the current mode - promiscuous or connected. Change it when you need to change states. Only attempt to connect to wifi when the variable says you want to be in connected mode.
There may be some code you need to run to turn off promiscuous mode when you change states, before you connect to a wifi access point.
If you're using wifi_sniffer_init() from the example you linked to, that code isn't meant to be run in an Arduino Core application. It does some network initialization that the Arduino Core will also do. It may not be safe to do that twice (it might work, it might not... but it's definitely not intended to be done that way).
You're setting an interrupt handle chUpdate() which you didn't share. I'd bet that's the cause of your Core Panics. You can do very little in an interrupt handler. You definitely can't call most Arduino Core functions or most ESP-IDF functions. Most code isn't protected against interrupts, so the timer interrupt can occur while data structures are in an inconsistent state. Re-entering code can corrupt the data structures and cause the kind of crash you described. You're best off setting a volatile variable and waking up a task that will do the work you need done while not in the interrupt handler.
Finally, you should call WiFi.mode(WIFI_STA); before you call WiFi.begin().
For anyone confused about why you can't connect to a wifi network while in promiscuous mode - what ESP8266 and ESP32 call "promiscuous mode" is really "wifi monitor mode", which lets you monitor a wifi radio channel and see all wifi frames sent on it. They use the term "promiscuous mode" differently from the rest of the industry. Usually "promiscuous mode" means seeing all the packets being sent on the network (wifi or hardwired) that you're connected to.
I know that it's been just over a year but THANKS to the info provided by romkey I think I solved this problem within my app by calling this routine before connecting to WiFi to upload MAC data.
void end_Scan_WiFi() {
esp_wifi_set_promiscuous(false);
esp_wifi_stop();
}
Followed by this ...
WiFi.mode(WIFI_STA);
WiFi.begin(ssid,pass);
I got this shield below which fits great with my Arduino Uno. Moreover, I have managed to upload code to the shield with a serial adapter and send/receive UDP messages. As the picture shows this shield goes right on top of the Arduino UNO.
My ESP8266 Shield
The problem is that the communication between Arduino and the Shield is very slow.
For example, I use the code below to Arduino to write something using Serial (115200).
void loop() {
writeString("Hello!");
delay(1000);
}
Then I use a simple code to the ESP8266 shield to read the data from Arduino and send it via UDP (writeString is just a simple converter).
Udp.beginPacket(ip, localUdpPort);
writeString(Serial.readString());
Udp.endPacket();
void writeString(String stringData) {
for (int i = 0; i < stringData.length(); i++) {
Serial.write(stringData[i]);
// Push each char 1 by 1 on each loop pass
}
}
It works fine, the "Hello!" string is read from the ESP8266 shield and sent using UDP. The problem is that if I put anything below 1,000 ms of delay in the Arduino, the ESP shield does not read anything, which is strange considering that the shield is on the top of the Arduino with no restrictions between serial communication.
From https://www.arduino.cc/en/Serial/ReadString:
Serial.readString() reads characters from the serial buffer into a string. The function terminates if it times out (see setTimeout()).
From https://www.arduino.cc/en/Serial/SetTimeout:
It defaults to 1000 milliseconds.
So because you use this Serial.readString() thing with the default timeout there will always be a delay of 1000 ms before the string is received.
If you insist on messing with String, there is a similar function: Serial.readStringUntil():
The function terminates if the terminator character is detected or it times out
So you have it detect the terminating character of the string:
Serial.readStringUntil('\n');
In this case the timeout is merely a safeguard to keep the code from hanging if for some reason the terminating character should not arrive but this should never happen if things are working correctly so there are no unnecessary delays.
The trouble with that is your current code doesn't send a terminator but that's easy enough to fix.
I recommend that you consider using the more efficient and safer string (char array) rather than String. There is an equivalent function: Serial.readBytesUntil().
I think my problem would best be explained with an example. Here is some Arduino Code:
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.println(millis());
delay(1);
}
...and here is the output:
Now, those numbers at the beginning aren't just your regular old first-bytes-of-serial-data-are-always-messed-up numbers. Those numbers are left over from the last time I opened the serial monitor. How do I know this? Because I counted how many seconds had gone by since I last opened the serial monitor, and it all matched up.
So here's the big questioroonie,
How do I make this stop? It's breaking my java program that's supposed to read data from the Arduino's current "run instance"(I don't know the correct term), and not the last one.
There is nothing wrong with your Arduino. Remember, the USB connection is not a real RS-232 serial interface, it's simulated through USB. And that USB connection is controlled by a chip with its own buffer.
Bad news is, nothing you can do if you're using the USB cable to carry your RS-232 signals, other than to send some "filler characters" to purge the buffer. Too many buffers everywhere :) Send some terminal emulation commands like "clear the screen" and "form feed" etc. to empty the pipeline of stuff in the buffer.
Which reminds me -- we should all quit using the crummy Arduino serial monitor and be vocal about it. Instead, use TeraTerm or Putty or any other good-quality terminal emulator of your choice.
Or connect with real RS232 and an adapter and cable. My development machine has three serial ports, but that's what I do. Often computers today don't even have one handy.
I need to communicate with a device using Arduino through RS232, I have everything setup and working. My problem is that the company that made the device a I need to communicate with told me the following:
1) The data sent through serial have one start and stop bit and is the hex "7E".
2) On the end of the message is appended a CRC hash
The device uses a request/response protocol, so the Arduino board must send the data in order to receive something. I've set the start and stop bit as ASCII characters put the data and CRC in between and simply Serial.write(data); I've setup two Arduino boards and both communicating this way. Everything went fine.
But with the device itself it's not working. The device sends back a response like "CE 0F " with spaces in between. So I start searching more about serial communication and found about start and stop bits and parity and start wondering if I'm sending and receiving data in the way the device expects.
To send and receive I used the basics tutorials like:
if (Serial.available() > 0) {
// read the incoming byte:
incomingByte = Serial.read();
// say what you got:
Serial.print("I received: ");
Serial.println(incomingByte, DEC);
}
So far I don't understand very well this type of communication. So I want to know why, for example some resources says that the start bit is 0 and the stop bit is 1, and in this case the start and stop bit is "7E".
I'm suspecting that this has something with:
https://en.wikipedia.org/wiki/High-Level_Data_Link_Control
I have a Bluetooth module connected to my RX(0) pin on the Arduino through which I am receiving data.
I am then printing that data using a Serial.write().
These two pins correspond to COM16 in my computer.
I am now able to receive these values into processing and print them again in processing after I set the COM port to 16 in processing.
Now I want to send a particular value out from processing back to Arduino again via serial communication. I figured I could do this with the software serial.
However, I have a few questions as to how the software serial works:
If I setup a software serial, what is the COM port for the software serial for which I can send values out from processing to the Arduino?
This is the command to set the COM port in processing.
String portName ="COM16";
myPort = new Serial(this, portName, 57600);
I then use myPort.write() to send some values back to the Arduino but how do I capture the values in the soft serial?
The best way is to use Serial Event
Minimal code:
void setup() {
Serial.begin(9600); // initialize serial
}
void loop() { // you need to define a loop function even
// if not used at all.
}
// This is an interrupt.
// Code below will only execute when something is received in the RX pin.
void serialEvent() {
while (Serial.available()) {
char inChar = (char)Serial.read(); // get the new byte
// here you can process the received byte
}
}
If you want to receive several bytes before processing them check the example in the link provided, where it concatenates the chars into a string until the \n byte is received
The advantage of this approach is that it uses interruptions, so you don't need to constantly check if something is received in the serial port.
PD: I see that in your code you use a baudrate of 57600. Just modify the code above to make sure you use the same speed, otherwise you will not receive anything (or even worse, you will receive garbage)
I would do it the other way around: use SoftSerial / AltSoftSerial for the Bluetooth module comms and the Hardware Serial for comms with Processing.
Bare in mind SoftSerial may not be as robust as a proper hardware serial port.Baud rate 57600 should work, but bare in mind higher baud rates may be unreliable.
Alternatively, if the budget/time allows it, you could use an Arduino that has multiple Serial ports (such Arduino Mega, Due, etc. which have Serial, Serial1, etc.)