Serial Interfacing of Arduino with Raspberry Pi to transmit 2 digit data - python

/*
String to Integer conversion
Reads a serial input string until it sees a newline, then converts
the string to a number if the characters are digits.
The circuit:
No external components needed.
created 29 Nov 2010
by Tom Igoe
This example code is in the public domain.
*/
#include <LiquidCrystal.h>
LiquidCrystal lcd(23, 22,13,12,11,10);
String inString = ""; // string to hold input
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(19200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
lcd.begin(20, 4);
// send an intro:
//println("\n\nString toInt():");
//Serial.println();
}
void loop() {
// Read serial input:
while (Serial.available() > 0) {
lcd.setCursor(0,0);
lcd.print("welcome");
int inChar = Serial.read();
if (isDigit(inChar)) {
// convert the incoming byte to a char
// and add it to the string:
inString += (char)inChar;
}
// if you get a newline, print the string,
// then the string's value:
if (inChar == '\n') {
int data = inString.toInt() ;
lcd.setCursor(0,1);
lcd.print("speed: ");
lcd.println(data);
if(data > 50){
lcd.setCursor(0,2);
lcd.print("Over speed");
}
else{
lcd.setCursor(0,2);
lcd.print("Under speed limit ");
}
// Serial.print("Value:");
//Serial.println(inString.toInt());
//Serial.print("String: ");
//Serial.println(inString);
// clear the string for new input:
inString = "";
}
}
}
This code works while using serial monitor with Arduino, but when run interface Arduino with Raspberry Pi, only "welcome" message come on LCD not data (2 digit number) that I transferred through Raspberry Pi, may be it is due to Python program on Raspberry Pi or any other reason.

You're checking for "\n" but never sending one. so you'll never trigger
if (inChar == '\n')

Related

Sending Float Values from Python to Arduino - Data in buffer but no values read

I am trying to send float values from Python on Windows to an Arduino. The first problem I encounter is that I cannot view the serial monitor when I think I am sending data from my Python script. I have read online that this is becuase only one application can manage the port at once: https://forum.arduino.cc/t/serial-communication-only-working-when-serial-monitor-is-opened/601107/12
However I have seen examples where the user is viewing the serial monitor to see data coming in over serial from Python and serial.print outs from the Arduino. So I am unsure what is the case... not being able to view the serial monitor sure does make debugging this senario hard.
My Python code:
import struct
import serial
import time
print('test123')
x=0.8
y=0.2
ser = serial.Serial('COM5', 9600, timeout=1)
#print(ser)
time.sleep(3)
def sendmess():
bin = struct.pack('ff',x,y) #Pack float value into 4 bytes
data = ser.write(bin)
#print(bin)
#print(len(bin))
#print([ "0x%02x" % b for b in bin])
#ser.close()
while True:
sendmess()
My Arduino Code:
int d = 250;
float incomingByte = 1;
void setup() {
// initialize the serial communication:
Serial.begin(9600);
pinMode(2, OUTPUT);
}
void loop() {
digitalWrite(2, HIGH);
delay(d);
digitalWrite(2, LOW);
delay(d);
// reply only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
d = 1000;
float incomingByte = Serial.read();
// say what you got:
Serial.println(incomingByte);
}
else{
Serial.println(incomingByte);
d = 20;
}
}
I see the LED flash every second so I know the serial buffer is >0, but I cannot get any data out :(.
Thanks in advance!
Mark
I have tried examples online, but I never get any data to display in the serial monitor. Nor can I say have a pin turn HIGH when I think I am getting the data I think I have sent. But without the Serial Monitor how can I debug this?
Unfortunately, you aren't able to connect more than two devices to the serial connection. However, using Python you should be able to read a response from your arduino and then print that to the python Terminal (or a file) and take a look at that.
The easiest way to do that would be to use pySerial and either ser.read(4) to read back 4 bytes of your float or ser.readline() which will look for either a '\n' which you can add to your arduino code, or a timeout. Then just print it to the python terminal with print().
As for actually reading the float, Serial.read() will only read in the next byte in the buffer. Since a float is 4 bytes long, you need to either read the 4 bytes into an array or use Serial.parseFloat()
Python Code
import struct
import serial
import time
x=0.8
y=0.2
ser = serial.Serial('COM5', 9600, timeout=1)
#print(ser)
time.sleep(3)
def sendmess():
bin = str(x) + str(y) #Pack float value into 4 bytes
data = ser.write(bin.encode())
echo = ser.readline()
print("Echo: " + echo)
#ser.close()
while True:
sendmess()
Arduino Code:
int d = 250;
float X;
float Y;
char buffer[40];
void setup() {
// initialize the serial communication:
Serial.begin(9600);
pinMode(2, OUTPUT);
}
void loop() {
digitalWrite(2, HIGH);
delay(d);
digitalWrite(2, LOW);
delay(d);
// reply only when you receive data:
if (Serial.available() > 0) {
// read the incoming byte:
d = 1000;
X = Serial.parseFloat();
Y = Serial.parseFloat();
// say what you got:
sprintf(buffer, "X: %f Y: %f", X, Y);
Serial.println(buffer);
}
else{
Serial.println(buffer);
d = 20;
}
}

How to send an int from Python to an Arduino in order to use it as an argument for the neopixel function setPixelcolor()?

I am trying to send an int number from Python to an Arduino using PySerial, using .write([data]) to send with Python and Serial.read() or Serial.readString() to recieve on the Arduino, then .setPixelColor() and .show() to light a LED on a matrix which position corresponds to the int sent by the arduino (I am using the Duinofun Neopixel Shield).
But It does not seem to work properly, and I can't use the Serial Monitor as I am sending my data as the port would be busy.
I have tried to input a number myself using Serial.readString() then converting the string to an int and finally putting in into my function that displays the LED.
It does work properly when I do this, but when I send some data over, all the previously lit LEDs suddenly switch off which can only be caused by a reset of the Arduino board as far as I know.
This is the python code, it simply sends an int chosen by the user
import serial
a = int(input('Enter pixel position : '))
ser = serial.Serial("COM16", 9600)
ser.write([a])
And this is the part of the Arduino program that reads the incoming data.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(40, 6, NEO_GRB + NEO_KHZ800);
void setup() {
// put your setup code here, to run once:
pixels.begin();
Serial.begin(9600);
}
void loop() {
String a = Serial.readString();
int b = a.toInt();
pixels.setPixelColor(b, 30,30,30);
pixels.show();
Serial.println(a);
delay(1000);
}
All the LED switch off when I send some data, except the first LED which position corresponds to a 0 used in the .setPixelColor() function.
Problem is, the LED should light to the corresponding int sent by Python (e.g light the fifth LED for an int of 4).
You don't need to send an int from your Python script. Just send a string and then convert it back to int on your Arduino. Also, you can verify the number simply on your Arduino code if the received value is valid.
Another problem with your Arduino code is you are not checking the Serial port availability which would return an empty string by Serial.readString().
A simple approach is shown below but you can extend it for other pixels.
Python script:
import serial
ser = serial.Serial("COM16", 9600)
while True:
input_value = input('Enter pixel position: ')
ser.write(input_value.encode())
Arduino code:
#define MIN_PIXEL_RANGE 0
#define MAX_PIXEL_RANGE 100
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(40, 6, NEO_GRB + NEO_KHZ800);
void setup()
{
// put your setup code here, to run once:
pixels.begin();
Serial.begin(9600);
}
void loop()
{
if (Serial.available())
{
String a = Serial.readString();
Serial.print("Received Value: ");
Serial.println(a);
int b = a.toInt();
if ((b >= MIN_PIXEL_RANGE) && (b <= MAX_PIXEL_RANGE))
{
pixels.setPixelColor(b, 30, 30, 30);
pixels.show();
delay(1000);
}
}
}
You can communicate between Ardinos and Python really easily and reliably if you use the pip-installable package pySerialTransfer. The package is non-blocking, easy to use, supports variable length packets, automatically parses packets, and uses CRC-8 for packet corruption detection.
Here's an example Python script:
from pySerialTransfer import pySerialTransfer as txfer
if __name__ == '__main__':
try:
link = txfer.SerialTransfer('COM13')
link.txBuff[0] = 'h'
link.txBuff[1] = 'i'
link.txBuff[2] = '\n'
link.send(3)
while not link.available():
if link.status < 0:
print('ERROR: {}'.format(link.status))
print('Response received:')
response = ''
for index in range(link.bytesRead):
response += chr(link.rxBuff[index])
print(response)
link.close()
except KeyboardInterrupt:
link.close()
Note that the Arduino will need to use the library SerialTransfer.h. You can install SerialTransfer.h using the Arduino IDE's Libraries Manager.
Here's an example Arduino sketch:
#include "SerialTransfer.h"
SerialTransfer myTransfer;
void setup()
{
Serial.begin(115200);
Serial1.begin(115200);
myTransfer.begin(Serial1);
}
void loop()
{
myTransfer.txBuff[0] = 'h';
myTransfer.txBuff[1] = 'i';
myTransfer.txBuff[2] = '\n';
myTransfer.sendData(3);
delay(100);
if(myTransfer.available())
{
Serial.println("New Data");
for(byte i = 0; i < myTransfer.bytesRead; i++)
Serial.write(myTransfer.rxBuff[i]);
Serial.println();
}
else if(myTransfer.status < 0)
{
Serial.print("ERROR: ");
Serial.println(myTransfer.status);
}
}
Lastly, note that you can transmit ints, floats, chars, etc. using the combination of these libraries!

Arduino to Raspberry Pi serial communication creates only random chars after a few seconds

For my project I need a Raspberry Pi to communicate with several peripheral components, two of them are Arduinos. The one which causes my problem is a Pro Mini 3.3 V ATmega328. The Arduino receives input from two sensors and transfer the data to the Raspberry Pi via serial. A Python code with the serial-package is used on the Raspberry which establishes a connection every 50 ms.
When the input to the Raspberry is printed, the first few lines are correct but after about two, three seconds the printed lines are random chars.
My Python code looks like this:
import serial
ser = serial.Serial('/dev/ttyS0', 115200, timeout=1)
if ser.isOpen():
ser.close()
ser.open()
...
# loop
try:
ser.write("1")
ser_line = ser.readline()
print ser_line
...
The Arduino code:
#include <Wire.h>
#include "SparkFunHTU21D.h"
#include <FDC1004.h>
FDC1004 fdc(FDC1004_400HZ);
HTU21D myHumidity;
int capdac = 0;
void setup() {
Wire.begin();
Serial.begin(115200);
myHumidity.begin();
}
void loop() {
String dataString = "";
dataString += String(myHumidity.readHumidity());
dataString += " ";
dataString += String(myHumidity.readTemperature());
dataString += " ";
for (uint8_t i = 0; i < 4; i++){
uint8_t measurement = 0;
uint8_t channel = i;
fdc.configureMeasurementSingle(measurement, channel, capdac);
fdc.triggerSingleMeasurement(measurement, FDC1004_400HZ);
//wait for completion
delay(15);
uint16_t value[2];
if (! fdc.readMeasurement(measurement, value)) {
int16_t msb = (int16_t) value[0];
int32_t capacitance = ((int32_t) 457) * ((int32_t) msb);
capacitance /= 1000; //in femtofarads
capacitance += ((int32_t) 3028) * ((int32_t) capdac);
dataString += String(capacitance);
dataString += " ";
int16_t upper_bound = 0x4000;
int16_t lower_bound = -1 * upper_bound;
if (msb > upper_bound) {
if (capdac < FDC1004_CAPDAC_MAX)
capdac++;
} else if (msb < lower_bound && capdac > 0) {
capdac--;
}
}
}
Serial.println(dataString);
delay(20); // delay in between reads for stability
}
The output of this loop looks like:
So the output loses accuracy and become random chars after about six lines and the output doesn't recover. When I print the serial output in the Arduino's serial monitor the output stays correct all the time. After several tests I run out of ideas. Has anyone a solution for this problem or experienced a similar behavior?

How to read pot connected to arduino from Rpi using python and i2c

I'm trying to write analog readings from a potentiometer connected to an Arduino and read those values using I2C from python on an RPi. I have gotten Arduino to Arduino to work using the code below. What I cannot seem to do correctly is write two bytes from the Arduino and read two bytes from the RPi.
Arduino Master code:
#include <Wire.h>
#define SLAVE_ADDRESS 0x2a
void setup()
{
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
}
void loop()
{
Wire.requestFrom(SLAVE_ADDRESS, 2); // request 2 bytes from slave
byte loByte;
byte hiByte;
if(Wire.available() >= 2) // slave may send less than requested
{
hiByte = Wire.read();
loByte = Wire.read();
}
int val = (hiByte << 8) + loByte;
Serial.print("read value:");
Serial.println(val);
delay(500);
}
Arduino Slave code:
#include <Wire.h>
#include <stdlib.h>
#define SLAVE_ADDRESS 0x2a
//#define potPin 0
int readVal;
byte hi;
byte lo;
void setup()
{
// Communication I2C
Wire.begin(SLAVE_ADDRESS);
Wire.onRequest(requestEvent); // register event
Serial.begin(9600);
}
void loop()
{
readVal = analogRead(A2);
Serial.println(readVal);
hi = highByte(readVal);
lo = lowByte(readVal);
}
void requestEvent()
{
byte buf [2];
buf [0] = hi;
buf [1] = lo;
Wire.write(buf, sizeof buf); // send 2-byte response
}
The closest I have gotten reading from an RPi is:
RPi Master code:
import smbus
import time
bus = smbus.SMBus(1)
address = 0x2a
while True:
bus.write_byte(address, 1)
number = bus.read_byte(address)
print(number)
time.sleep(1)
Arduino slave code:
#include <Wire.h>
#define SLAVE_ADDRESS 0x2a
int number = 0;
void setup() {
Wire.begin(SLAVE_ADDRESS);
Wire.onReceive(receiveData);
Wire.onRequest(sendData);
}
void loop() {
}
void receiveData(int byteCount){
while(Wire.available()) {
number = Wire.read();
number = analogRead(A2);
}
}
void sendData(){
Wire.write(number);
}
I seem to be able to get 0-255, but after 255 the value starts again. No doubt there is a more precise way to say I am only getting one byte of data or something along those lines. Ultimately I want to have 2 pots connected to the Arduino feeding readings into the RPi.
On Arduino, analogRead returns an int value in the range 0-1023. On this hardware, an int is two bytes.
However, the form of Wire.write that you use in the sendData function only writes a single byte, discarding part of the integer.
There are basically two solutions.
The simplest would be to take the return value of analogRead, divide it by 4 and cast it into a byte. Send that out with Wire.write. This does reduce the resolution of the value of the pot-meter, but is it a very simple solution.
The other was is to send an integer value over the wire. Since you're reading bytes on the RPi, you cannot know if you are reading the first or second byte of an integer. So you would probably have to use a signal to indicate the start of a two-byte sequence. You would also have to take the endian-ness of both platform into account. All in all, this is much more complicated.
Thanks for the feedback. It helped me think through this a bit more and do more digging. This is what I have working.
Arduino side for writing:
#include <Wire.h>
#define SLAVE_ADDRESS 0x2a
#define pot1pin A2
#define pot2pin A3
byte pot1byte;
byte pot2byte;
void setup()
{
Wire.begin(SLAVE_ADDRESS);
Wire.onRequest(requestEvent);
}
void loop() {
int pot1int = analogRead(pot1pin);
int pot2int = analogRead(pot2pin);
pot1byte = map(pot1int, 0, 1024, 0, 255);
pot2byte = map(pot2int, 0, 1024, 0, 255);
}
void requestEvent()
{
Wire.write(pot1byte);
delay(30);
Wire.write(pot2byte);
}
RPi side for reading:
import smbus
bus = smbus.SMBus(1)
address = 0x2a
while (1):
block = bus.read_i2c_block_data(address, 0, 2) # Returned value is a list of 2 bytes
print(block)
As you can see I am reading 2 pots, converting the output to 0-255, writing to the I2C bus and then reading the 2 bytes on the RPi side. I did have to change the Arduino delay value during testing because I was getting the error "IOError: [Errno 5] Input/output error" after a few minutes. Now maybe I will go back and write 2 bytes per pot and read 4 bytes so I don't lose and resolution.

Difficulty with sending sensor data

I try to send data over an arduino Ethernet shield to client (python on PC)
the problem that i had is when i read like example the pin A0 in arduino i get 1023 but when i send this value to python i get 49152...
arduino code
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // Enter a MAC address for your controller below.
IPAddress ip(192,168,0,101);
IPAddress gateway(192,168,0,254);
IPAddress subnet(255,255,255,0);
unsigned int UDPport = 5000;// local port to listen for UDP packets
IPAddress UDPServer(192,168,0,100); // destination device server
const int UDP_PACKET_SIZE= 48;
byte packetBuffer[ UDP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
unsigned int noChange = 0;
int UDPCount = 0;
EthernetUDP Udp;
unsigned long currentTime;
unsigned long secondTime;
unsigned long msPerSecond = 100UL;
float temperature;
float vitesse;
float charge;
float current;
void setup()
{
Serial.begin(9600);
Ethernet.begin(mac,ip,gateway,gateway,subnet);
Udp.begin(UDPport);
delay(1500);
currentTime=millis();
secondTime = currentTime;
}
void loop()
{
currentTime = millis();
getUDPpacket();
if(currentTime - secondTime > msPerSecond) {
temperature = analogRead(0); //read analog input on pin A0
vitesse = analogRead(1); //read analog input on pin A1
charge = analogRead(2); //read analog input on pin A2
current = analogRead(3); //read analog input on pin A3
Serial.println(temperature);
sendUDPpacket(UDPServer); // send an NTP packet to a time server
secondTime += msPerSecond;
}
}
unsigned int udpCount = 0;
unsigned long sendUDPpacket(IPAddress& address)
{
udpCount++;
memset(packetBuffer, 0, UDP_PACKET_SIZE); sprintf((char*)packetBuffer,"%u,%u,%u,%u",temperature,vitesse,charge,current);
Udp.beginPacket(address, UDPport);
Udp.write(packetBuffer,UDP_PACKET_SIZE);
Udp.endPacket();
}
void getUDPpacket() {
if ( Udp.parsePacket() ) {
if(Udp.remoteIP() == UDPServer) {
Serial.print(F("UDP IP OK "));
}
else {
Serial.println(F("UDP IP Bad"));
return;
}
if(Udp.remotePort() == UDPport) {
Serial.println(F("Port OK"));
}
else {
Serial.println(F("Port Bad"));
return;
}
Udp.read(packetBuffer,UDP_PACKET_SIZE); // read the packet into the buffer
Serial.print(F("Received: "));
Serial.println((char*)packetBuffer);
}
}
the python code
import socket
import time
UDP_IP = "192.168.0.100"
UDP_PORT = 5000
sock = socket.socket(socket.AF_INET, # Internet
socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))
while True:
data, addr = sock.recvfrom(48)
print data
time.sleep(5)
i think the problem is in this line
sprintf((char*)packetBuffer,"%u,%u,%u,%u",temperature,vitesse,charge,current);
but i dont know what to do
You should enable all warnings (eg in gcc use -Wall) when you compile your C code. That way, it would warn you when you try to print floats with the %u unsigned integer format specifier, as that leads to undefined behaviour.
You should either a) convert temperature, vitesse, charge, and currentto unsigned integer before passing them to sprintf(), or b) change the format specifier to something like %f so that sprintf() knows to expect floating-point data.
Also you should include <stdio.h> so that your program has a prototype for sprintf(); I assume that those headers you have included contain prototypes & constants relevant to the Ethernet & Arduino port IO functions.

Categories