Python Tkinter Slider Widget write Value over I2C - python

Ok I have been searching for an example for a while now can not seem to find any examples of relaying the tkinter slider values over I2C to Arduino. So far I have not tried to communicate with the Arduino yet. Ill cross that bridge next; for now I just want to figure out how to write the slide widget values and send over I2C.
Here is a simple GUI slider widget in Python 2 with what I believe is the correct setup for I2C communications. Ive updated the Rpi to set up the I2C as well. What I want to do in Arduino is simply read the values 0 to 180 for a servo control. It is important that it just writes values or in some way that can be used for an input. I have other code in the arduino that drives the same servo, if other conditions are met and this would then be ignored.
from Tkinter import*
import RPi.GPIO as GPIO
import time
import smbus
bus = smbus.SMBus=(1)
SLAVE_ADDRESS = 0x04
class App:
def __init__(self, master):
def SendScaleReading(self):
S = scale.get()# Now how do we write this and get the Scale Value and send it??
bus(SLAVE_ADDRESS, ord('S'))#According to an example this should be
#"bus.write_byte(SLAVE_ADDRESS, ord('S'))"
frame = Frame(master)
frame.pack()
scale = Scale(frame, from_=0, to=180, orient=HORIZONTAL, command=SendScaleReading)
scale.grid(row=1, column=1)
root = Tk()
root.wm_title('I2C servo control')
app = App(root)
root.geometry("200x50+0+0")
root.mainloop()

Ok a friend helped me out with and I wasnt too far off. Once I got this far I only a small issue with an IO error. I was getting [Errno 5] IO errors. Make sure you have a ground connected between the Arduino and Pi. When I searched this seemed to be overlooked by the many fixes out there offered. You need SDA, SLA and Gnd all connected.
So anyways here is the code that is working running an arduino i2c blink sketch. This will blink the led on pin 13 faster or slower based on the slider input in from the Rpi over I2C. I am going to try and write this code to control a servo next and if successful I will also post that code.
Rpi / python2 code below:
from Tkinter import*
import RPi.GPIO as GPIO
import time
import smbus
bus = smbus.SMBus(1)
SLAVE_ADDRESS = 0x28
class App:
def __init__(self, master):
def SendScaleReading(self):
S = scale.get()
print("we have" );
print( S )
bus.write_byte_data(SLAVE_ADDRESS, S, S )
frame = Frame(master)
frame.pack()
scale = Scale(frame, from_=0, to=180, orient=HORIZONTAL, command=SendScaleReading)
scale.grid(row=1, column=1)
root = Tk()
root.wm_title('I2C servo control')
app = App(root)
root.geometry("200x50+0+0")
root.mainloop()
Arduino i2c wire Blink sketch below:
#include <Wire.h>
// unique address for this I2C slave device
#define ADDRESS 0x28
// constants won't change. Used here to
// set pin numbers:
const int ledPin = 13; // the number of the LED pin
// Variables will change:
int ledState = LOW; // ledState used to set the LED
long previousMillis = 0; // will store last time LED was updated
// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
volatile long interval = 1000; // interval at which to blink (milliseconds)
void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
Wire.begin(ADDRESS); // join i2c bus with address #4
Wire.onReceive(receiveEvent); // register event
Wire.onRequest(requestEvent); // register the request handler
}
//gets called when I2C read occurs
void requestEvent() {
//any request for data will return 0x14 (random number i picked for testing)
Wire.write( 0x14 );
}
// get called when I2C write occurs
void receiveEvent(int howMany) {
//just going to support 1 byte commands for now
if (howMany >0 ) {
int c = Wire.read();
interval = c * 10;
}
while (Wire.available() > 0 ) {
Wire.read();
}
}
void loop()
{
// here is where you'd put code that needs to be running all the time.
// check to see if it's time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
// blink the LED.
unsigned long currentMillis = millis();
if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;
// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;
// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}

Ok well the servo code ended up being rather simple. Im sure it can be cleaned up so anyone with the know how please feel free to make improvements.
Arduino I2C Driven Servo Sketch is below:
#include <Wire.h>
#include <Servo.h>
// unique address for this I2C slave device
#define ADDRESS 0x28
Servo myservo;
int pos = 0;
void setup() {
myservo.attach(9);
Wire.begin(ADDRESS); // join i2c bus with address #4
Wire.onReceive(receiveEvent); // register event
Wire.onRequest(requestEvent); // register the request handler
}
//gets called when I2C read occurs
void requestEvent() {
//any request for data will return 0x14 (random number i picked for testing)
Wire.write( 0x14 );
}
// get called when I2C write occurs
void receiveEvent(int Pos) {
int val = Wire.read();
pos = val;
while (Wire.available() > 0 ) {
Wire.read();
}
}
void loop()
{
myservo.write(pos);
delay(15);
}

Related

Read multiple arduino's using a Raspberry Pi 3 through USB

I have 3 arduino's that have RC522 rfid readers attached to them. They each have their own power supplies and they are connected to a raspberry pi 3 via the usb ports. I am getting really inconsistent results when running the reader in python. It does appear all 3 are reading but the loop does some weird things. Sometimes the chip code keeps repeating after the chip is removed and other times it works properly. There does not appear to be any consistency with which arduino is behaving oddly either.
Any help would be greatly appreciated!!!
Here is the arduino code (The same code was copied to each arduino with the exception of the initial println that indicates which arduino is connected).
#include <SPI.h>
#include <MFRC522.h>
#define RST_PIN 5 // Configurable, see typical pin layout above
#define SS_PIN 53 // Configurable, see typical pin layout above
#define MOSI_PIN 51
#define MISO_PIN 50
#define SCK_PIN 52
MFRC522 rfid(SS_PIN, RST_PIN); // Instance of the class
MFRC522::MIFARE_Key key;
String read_rfid;
void setup() {
Serial.begin(9600);
SPI.begin(); // Init SPI bus
rfid.PCD_Init(); // Init MFRC522
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
Serial.println(F("The Toy Maker's Sanctuary RDIF Reader 1 Online."));
//Serial.println(F("Using the following key:"));
//printHex(key.keyByte, MFRC522::MF_KEY_SIZE);
}
void loop() {
// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if ( ! rfid.PICC_IsNewCardPresent())
return;
// Select one of the cards
if ( ! rfid.PICC_ReadCardSerial())
return;
dump_byte_array(rfid.uid.uidByte, rfid.uid.size);
Serial.println(read_rfid);
}
/*
Helper routine to dump a byte array as hex values to Serial.
*/
void dump_byte_array(byte *buffer, byte bufferSize) {
read_rfid = "";
for (byte i = 0; i < bufferSize; i++) {
read_rfid = read_rfid + String(buffer[i], HEX);
}
}
Here is the python code
import serial
ser0=serial.Serial("/dev/ttyACM0", 9600, timeout=1)
ser1=serial.Serial("/dev/ttyACM1", 9600, timeout=1)
ser2=serial.Serial("/dev/ttyACM2", 9600, timeout=1)
ser0.baudrate=9600
ser1.baudrate=9600
ser2.baudrate=9600
read_ser0=""
read_ser1=""
read_ser2=""
while True:
read_ser0=ser0.readline()
print("0: ",read_ser0)
read_ser1=ser1.readline()
print("1: ",read_ser1)
read_ser2=ser2.readline()
print("2: ",read_ser2)
read_ser0=""
read_ser1=""
read_ser2=""

How to read a data struct sent serially from an arduino?

I'm struggling to read a binary data struct with Python3 sent serially from an Arduino Mega 2560.
Arduino Code:
char userInput;
bool ledState = false;
typedef struct
{
unsigned long logTimeStart;
unsigned long frequencyOne;
unsigned long frequencyTwo;
unsigned long frequencyThree;
unsigned long frequencyFour;
unsigned short int revPowerOne;
unsigned short int revPowerTwo;
unsigned short int revPowerThree;
unsigned short int revPowerFour;
unsigned short int tempSensor;
} SDLogger;
SDLogger dataLog;
void setup()
{
Serial.begin(9600);
bitWrite(DDRB, 7, HIGH); // Set LED DDR high on port B, pin 7
dataLog.logTimeStart = millis();
dataLog.frequencyOne = 4000000000; // PREDEFINED VALUES FOR TESTING
dataLog.frequencyTwo = 4000000000;
dataLog.frequencyThree = 4000000000;
dataLog.frequencyFour = 4000000000;
dataLog.revPowerOne = 65000; // PREDEFINED VALUES FOR TESTING
dataLog.revPowerTwo = 65000;
dataLog.revPowerThree = 65000;
dataLog.revPowerFour = 65000;
dataLog.tempSensor = 65000;
Serial.println("<Arduino is ready>");
}
void loop()
{
checkForSerialMessage();
delay(1000);
}
void checkForSerialMessage()
{
Serial.println("checking serial...");
if (Serial.available() > 0)
{
userInput = Serial.read();
if (userInput == 'a')
{
ledState = !ledState;
bitWrite(PORTB, 7, ledState);
Serial.println("led");
}
if (userInput == 'b')
{
Serial.write((byte*)&dataLog, sizeof(dataLog));
}
}
}
I believe the size of the struct is 30 bytes. My goal is to have the Arduino check the buffer intermittently for a specific character. When that character is received, the Arduino sends a defined data struct to my python interface, which is then unpacked and decoded into something readable by humans. I wrote an alternative command which toggles the Arduino's built-in LED. This was done to understand simple serial interfacing between the software. The LED toggle seems to function as expected, but not the struct commands.
Python Code
from tkinter import *
from struct import *
import serial
import time
ser = serial.Serial('COM5', baudrate=9600, timeout=1)
root = Tk() # root widget, this has to be first before any other widgets.
BINARY_STRFORMAT = "LLLLLHHHHH" # Format to match arduino struct
print(calcsize(BINARY_STRFORMAT)) # RESULT IS 30
def toggleLED():
ser.write(b'a')
arduinoData = ser.readline().decode('ascii') # readline functions will read until it sees a newline \n
# .decode('ascii')
print(arduinoData)
def toggleSerial():
ser.write(b'b')
arduinoData = ser.readline()
print(arduinoData)
unpacked = unpack(BINARY_STRFORMAT, arduinoData)
# in tkinter, everything is a widget:
header = Label(root, text="Read data from Arduino")
led_on = Button(root, text="led on", command=toggleLED)
read_serial = Button(root, text="enable data stream", command=toggleSerial)
# showing Label widget onto screen
header.pack()
led_on.pack()
read_serial.pack()
root.mainloop()
When I run the python code, I can use my rudimentary tkinter GUI to toggle the Arduino's LED. But when I attempt to request the data struct, I get a struct error:
struct.error: unpack requires a buffer of 30 bytes
I tried to read through the pySerial documentation to see if I could improve how my serial data is received, but I could not find struct references. I'm failing to understand how the struct is sent/received and how it should be read in python.
I'm relatively new to software engineering and serial communication, so please let me know if I need to clarify anything here.
Update: thanks to #martineau and #Edward, we learned that Serial.println() was writing data to the same port which was trying to transmit/receive the data struct. This overlap was interfering with the python GUI's ability to parse and decode the binary struct. When the Serial.println() was removed, the python GUI received the expected 30-byte struct.

Remote I/O Error on I2C Bus Raspi Master Arduino Mega Slave

I have connected my raspi (Master) with Arduino Mega (Slave) and want to continuously transfer the analog readings from analog pins A0-A3 to the raspi. Program runs for some time and the crashes giving me:
[Errno 121] Remote I/O Error
I am suspecting 2 things could be the problem: 1) The while loop => longer sleep times result in a longer running time until the program crashes 2) I read somewhere that the wire library does an onReceive command before it does an onRequest?
Here is my Arduino and raspi code:
Arduino Mega Code
#include
int SLAVE_ADDRESS = 0x08;
int analogPin1 = A0;
int analogPin2 = A1;
int analogPin3 = A2;
int analogPin4 = A3;
void setup(){
Wire.begin(SLAVE_ADDRESS);
Wire.onRequest(sendAnalogReading);
}
void loop(){
}
void sendAnalogReading(){
uint16_t reading1 = analogRead(analogPin1);
uint16_t reading2 = analogRead(analogPin2);
uint16_t reading3 = analogRead(analogPin3);
uint16_t reading4 = analogRead(analogPin4);
Wire.write((char *) &reading1, 2);
Wire.write((char *) &reading2, 2);
Wire.write((char *) &reading3, 2);
Wire.write((char *) &reading4, 2);
}
This is the script on my Raspi:
import smbus
import time
bus = smbus.SMBus(1)
SLAVE_ADDRESS = 0x08
def requestreading():
block = bus.read_i2c_block_data((SLAVE_ADDRESS), 0, 8)
print(block)
while True:
#var = input("Press any key for reading: ")
requestreading()
time.sleep(0.1)
The could should run continuously however i feel the actual runtime until it crashes depends on 2 factors. Length of time.sleep() in my while loop and if the measured values change much.

Pyserial not getting new value from Arduino

I'm trying to stream the data from an ultrasonic rangefinder on an Arduino to my laptop. I seem to be having an issue with getting the new value from the Arduino. When I launch the Python script it starts printing the data just like I hoped. However this data does not change when I change the distance the sensor sees, it's almost like the serial.readline() is stuck on one of the first values. I'm new to this serial communication stuff so any help is greatly appreciated!
The code is below and for sanity I did check that the sensor is working with the serial monitor in the Arduino IDE.
import numpy
import serial
import time
import sys
import cv2
import pickle
#set up the camera stuff
cap = cv2.VideoCapture(0)
#container for images
images=[]
#container for distances
distances=[]
#first frame number
frame_num=1
#setup the serial connection and pause to establish it
ser = serial.Serial('/dev/cu.usbmodem1421', 9600,timeout=1)
time.sleep(5)
while True:
try:
#grab and image
ret,frame=cap.read()
#grab the distance
distance=ser.readline()
print(distance)
#process the image to a gray and 1241,376
#gray=cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#gray_resized=cv2.resize(gray,(1241,376))
#cv2.imshow("FRAME",gray_resized)
#print(distance)
#images.append([frame_num,gray_resized])
#distances.append([frame_num,distance])
#ser.flush()
except KeyboardInterrupt:
#pickle.dump( images, open( "save.p", "wb" ) )
#pickle.dump( distances, open( "save.p", "wb" ) )
sys.exit()
Arduino code:
// defines pins numbers
const int trigPin = 7;
const int echoPin = 8;
// defines variables
long duration;
int distance;
void setup() {
pinMode(trigPin, OUTPUT); // Sets the trigPin as an Output
pinMode(echoPin, INPUT); // Sets the echoPin as an Input
Serial.begin(9600); // Starts the serial communication
delayMicroseconds(50);
}
void loop() {
// Clears the trigPin
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
// Sets the trigPin on HIGH state for 10 micro seconds
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Reads the echoPin, returns the sound wave travel time in microseconds
duration = pulseIn(echoPin, HIGH);
// Calculating the distance in CM
distance= duration*0.034/2;
String distance_out=String(distance);
// Prints the distance on the Serial Monitor in CM
Serial.println(distance);
Serial.flush();
//Serial.print("Distance: ");
//Serial.println(distance);
}
Actually, python's serial.readline() is blocking until it get a EOL, so if you do not have lock issue that mean the Arduino is writing in the buffer faster that your python script read it.
You should flush the buffer after reading to ensure (close to) realtime reading with serial.flushInput() or serial.reset_input_buffer() depending of your version

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.

Categories