I have connected Raspberry pi 2 model B with arduino uno via Bi-Directional Level shifter.
Raspberry pi GND ---------- GND Arduino
3.3v ---------- 5v
SCL ---------- A5
SDA ---------- A4
Hope my I2C connection is correct ?
and my Arduino is connected to 8-Channel Relay Board.
Now I have written code in which I can control the Relay board by Raspberry pi. For ex if i Press '1' the Relay 1 goes high.
Now I want to send data back from arduino to raspberry pi in order to cross check if Relay 1 is high or not, if Relay 1 is high then it should send some data back to Raspberry pi or else not.
My Rpi code is
import smbus
import time
# for RPI version 1, use "bus = smbus.SMBus(0)"
bus = smbus.SMBus(1)
# This is the address we setup in the Arduino Program
address = 0x04
def writeNumber(value):
bus.write_byte(address, value)
# bus.write_byte_data(address, 0, value)
return -1
def readNumber():
number = bus.read_byte(address)
# number = bus.read_byte_data(address, 1)
return number
while True:
var = input("")
if not var:
continue
writeNumber(var)
number = readNumber()
My Arduino code:
#include <Wire.h>
#define SLAVE_ADDRESS 0x04
#define RELAY1 9
int number = 0;
int state = 0;
void setup() {
pinMode(RELAY1, OUTPUT);
Serial.begin(9600); // start serial for output
// initialize i2c as slave
Wire.begin(SLAVE_ADDRESS);
// define callbacks for i2c communication
Wire.onReceive(receiveData);
Wire.onRequest(sendData);
Serial.println("Ready!");
}
void loop() {
delay(100);
}
// callback for received data
void receiveData(int byteCount){
while(Wire.available()) {
number = Wire.read();
Serial.print("data received: ");
Serial.println(number);
if (number == 1){
if (state == 0){
digitalWrite(RELAY1, HIGH); // set the LED on
state = 1;
}
else{
digitalWrite(RELAY1, LOW); // set the LED off
state = 0;
}
}
}
}
// callback for sending data
void sendData(){
Wire.write(number);
}
Now if I type 1 and due to some loose connection Relay 1 doesn't goes high, So in this case I want the arduino to take data from relay board and send it to Raspberry pi every time.
It will be great if someone can explain also that how it works.
Hope I was able to explain the problem. I have done lots of research but was not able to find some answer.
I am a beginner in python so please help me.
Thanks in advance.
The problem is that you are doing too much inside receiveData, which is called from the Interrupt Service Routine of the I2C utility code, twi.c. You must handle the data quickly, and don't call any other routines that depend on interrupts being enabled (they are disabled during this ISR).
This means you can't call Serial.print, and you can't call any other Wire sending methods. Even calling millis() or micros() is discouraged, as they do take a fair amount of time, and they depend on the TIMER interrupts being handled.
Of course, you are free call Wire.available() and Wire.read(). Actually, byteCount tells you how many bytes are available, so you don't need to call Wire.available() again.
Essentially, your receivedData routine can read the data inside the routine if you're quick about processing it. Otherwise, you can only set a (volatile) flag and then watch for it in loop. From what I see in your sketch, you could do something like this:
// variables that allow signalling between receiveData ISR and loop
volatile bool newData = false;
volatile uint8_t state = false;
// callback for received data
void receiveData(int byteCount)
{
// Read all the bytes; only the last one changes the relay state
while (byteCount-- > 0)
number = Wire.read();
if (state != number) {
state = number;
newData = true;
}
}
// callback for sending data
void sendData(){
Wire.write(number);
}
void loop()
{
if (newData) {
newData = false; // clear the flag for next time
if (number == 1){
digitalWrite(RELAY1, HIGH); // set the LED on
} else {
digitalWrite(RELAY1, LOW); // set the LED off
}
Serial.print("data received: ");
Serial.println( number );
}
}
The delay in loop is unnecessary, and may cause problems if you add something else to loop.
The volatile keyword keeps the compiler from optimizing loop. Without that keyword, the test for newData in loop would disappear because the compiler thinks that newData doesn't change during loop. Why test it? volatile newData tells the compiler that newData can change at any time, like during the receiveData ISR.
And be sure to print the number in the rpi code, as pholtz suggested!
In arduino code
change sendData() function like this
void sendData(){
int relay_status;
relay_status=digitalRead(4);
Wire.write(relay_status);
}
Also in hardware connect one 4th digital pin(or any other free I/O pins) to relay input.
hope it helps:)
Ok, looks like a pretty good start. Two things I want to suggest here.
First, in your python program you should print number so that you can see it's value changing. It's storing your feedback from the Arduino, so you want to display that feedback to the screen. This is as simple as changing number = readNumber() to print readNumber().
Second, in your Arduino program, are you sure that calling Wire.read() returns what you think it does? It looks to me like read() returns a byte. Chances are that when you type 1 it's really being sent as '1', not 1. Char vs. Int. Make sense?
So you might want to check if(number == '1') instead. Just my 2¢.
Your i2c bus is not connected properly. Remove the level shifter, and add 4.7k pull-ups to the 3.3v vcc on scl and sda lines. I2c chips only drive the line low, and require external resistors to pull the line high. This lets you mix voltage levels on your i2c bus fairly easily. Then you can go back to figuring out what your code is doing.
Related
I am trying to interface my SparkFun Qwiic Haptic Driver - DA7280 with Python3. My current set-up is as follows:
PC -USB to micro-USB-> SparkFun RedBoard Qwiic -Qwiic Cable-> Haptic Driver
I've trialed the accompanying Arduino sketch & managed to get the C++ code up and running fine; modulating the vibrator's intensity & frequency just fine.
Then, what I would like to do is to trigger a vibration pulse in time with some Python code. Such that, when python prints out a word, for example, a vibratory impulse would be triggered.
I have tried using pySerial to interface with the microcontroller, trigger the controller to run a pre-loaded script. This was worked fine with a simple C++ script to repeat an LED blink uploaded to the micro-controller:
/*
Blink
Turns an LED on for one second, then off for one second, repeatedly for 6 seconds
*/
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
// Open serial connection.
Serial.begin(9600);
Serial.write('1');
}
// the loop function waits until it receives an input from the serial port & will stop again when it receives a stop signal.
void loop() {
if(Serial.available() > 0){ // if data present, blink
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
Serial.write('0');
}
}
In conjunction with a Python script to trigger & close the controller:
# import necessary modules
import os
import serial
# connect to the arduino
## Boolean variable that will represent whether or not the arduino is connected
connected = False
## open the serial port that your ardiono is connected to.
ser = serial.Serial("COM8", 9600) # you may need to change this pending on how the board connects to the PC
## loop until the arduino tells us it is ready
while not connected:
serin = ser.read()
connected = True
## trigger the arduino to run the uploaded code
ser.write(1)
## Wait until the arduino tells us it is finished
while ser.read() == '1':
ser.read()
print(ser.read())
## trigger the arduino to run the uploaded code
ser.write(0)
## close the port and end the program
ser.close()
However, when I try replacing the LED-specific lines with commands to start and stop vibration (below) there are a few issues:
Although the controller waits for the go signal from Python as expected, Python blasts through the script in no time, not waiting for a stop from the controller.
The controller starts vibrating but there is no delays as expected between vibratory impulses & the vibrator never stops, even after python sends the stop trigger.
/*
Python triggered vibration.
Waits for Python to send a go signal at which point the vibration starts for a given duration of time.
*/
#include <Wire.h>
#include "Haptic_Driver.h" # this module is from the aforementioned Arduino sketch
Haptic_Driver hapDrive;
int event = 0;
void setup() {
// Open serial connection.
Wire.begin();
Serial.begin(9600);
if( !hapDrive.begin())
Serial.println("Could not communicate with Haptic Driver.");
else
Serial.println("Qwiic Haptic Driver DA7280 found!");
if( !hapDrive.defaultMotor() )
Serial.println("Could not set default settings.");
// Frequency tracking is done by the IC to ensure that the motor is hitting
// its resonant frequency. I found that restricting the PCB (squeezing)
// raises an error which stops operation because it can not reach resonance.
// I disable here to avoid this error.
hapDrive.enableFreqTrack(false);
Serial.println("Setting I2C Operation.");
hapDrive.setOperationMode(DRO_MODE);
Serial.println("Ready.");
Serial.write('1');
}
void loop(){
if(Serial.available() > 0){
hapDrive.setVibrate(25);
delay(1500);
hapDrive.setVibrate(0);
delay(1500);
Serial.write(0);
Serial.flush();
}
}
I am a novice when it comes to both micro-controllers & C++ so, forgive me for any major misunderstandings/errors. Also if anything is unclear in the above description please let me know.
Many Thanks,
Liam
I suspect at least part of the problem is that you are not clearing the contents of the read buffer, only checking if something is there. Serial.flush() i think that as of Arduino 1.00 (don't quote me on that) serial flush doesn't do anything to the incoming buffer.
try adding a var = Serial.read() in before your hapDrive.setVibrate(25); and see if that changes the functionality.
I also HEAVILY recommend interrupts for serial. There's a serial event example that's really comprehensive (although i seem to remember that's not actually interrupt driven in the classical microcontroller sense, but it's close enough!)
I am fairly new to hardware. I want to control an LED light using NodeMCU and Python. I uploaded an Arduino code in nodeMCU and then used pyserial library to get the serial output. But when I try to give input to the port, it doesn't work. I don't know where the problem is.
Here is the arduino code:
int inputVal = 0;
const int ledPin = 5; //D1 pin of NodeMCU
void setup() {
Serial.begin(9600);
delay(100);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, 0);
}
void loop() {
while(Serial.available()>0){
inputVal = Serial.read();
}
Serial.println(inputVal);
if(inputVal==1){
digitalWrite(ledPin, HIGH);
Serial.println("LED is ON");
}
else{
digitalWrite(ledPin, LOW);
Serial.println("LED is OFF");
}
Serial.println("");
}
Here is the python code:
import serial
global ser
ser = serial.Serial("COM8", baudrate=9600, timeout=10,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS)
while(True):
ser.write(bytes(1))
line = ser.readline()
print(line.decode('utf8'))
The output in python comes out to be:
0
LED is OFF
0
LED is OFF
0
LED is OFF
and so on. The ser.write() function isn't writing the value as 1 on the serial port. When I change the value of inputVal in Arduino code, the LED turns on and the output on arduino serial monitor comes as 1 LED is ON, which implies that the circuit connection and Arduino code is working fine.
I also noticed that the COM port that I am using can work with either python or arduino at a time. After uploading the arduino code with inputVal=1, the LED turned on and arduino serial monitor started displaying (1 LED is ON). But, as soon as I ran the python code, the led turned off and the python output came out to be 0 LED is OFF. Please help me.
Also, is there a way for me to control NodeMCU totally by python, without using arduino code first?
the output from python is correct. bytes(integer) creates an array of provided size, all initialized to null in your case size = 1, bytes(1), so the output that you have is 0x00 if you try bytes(10) the out put will be b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'.
what you need to do is to change ser.write(bytes(1)) to ser.write(bytes('1',encoding= 'utf-8')) that should work
Ok so what I'm trying to do is turn an LED on with one python script and off with another one. Now the problem I'm facing is my python script has to keep hanging for the LED to stay on. I can't figure out how to read something from serial, close the coms while leaving the LED on.
'g' is what I'm sending from the on python script and 'h' will be sent from the off python script.
The arduino:
void setup(){
Serial.begin(9600);
pinMode(13, OUTPUT);
Serial.write('g');
Serial.write('h');
}
void loop(){
if(Serial.read() == 'g' ){
digitalWrite(13, HIGH);
Serial.end();
}
if(Serial.read() == 'h' ){
digitalWrite(13, LOW);
Serial.end();
}
}
And the python
#! /usr/bin/python
## import the serial library
import serial
## Boolean variable that will represent
## whether or not the arduino is connected
connected = False
## open the serial port that your ardiono
## is connected to.
ser = serial.Serial("/dev/cu.wchusbserial1410", 9600)
## loop until the arduino is ready
while not connected:
serin = ser.read()
connected = True
ser.write("g")
while ser.read() == 'g':
ser.read()
## close the port
ser.close()
the 'while ser.read() parts at the bottom was just me messing about trying to figure out what I need but so far no such luck.
Thanks in advance!
In python code instead of using this serial command, simply use print command. Suppose you wanna send character g on the serial port then simply write:
print "g"
and it will be sent over to serial port. Worked for me while using Arduino YUN.
Thanks for the feedback. I used a different method and thought it would be a good idea to share the code incase anyone is interested in doing the same.
Python:
import serial
import time
arduino = serial.Serial('/dev/tty.wchusbserial1410', 9600)
time.sleep(0.1) # wait
print("initialising")
arduino.write('off') # turns LED off
print("LED OFF")
time.sleep(0.1) # wait
arduino.close() # close serial
This is the code used to turn the light off. If you want to turn it on, it's the same procedure but create another script replacing arduino.write('off') with arduino.write('on')
And Arduino:
int led = 13; // Pin 13
void setup()
{
pinMode(led, OUTPUT); // Set pin 13 as digital out
// Start up serial connection
Serial.begin(9600);
Serial.flush();
}
void loop()
{
String input = "";
// Read any serial input
while (Serial.available() > 0)
{
input += (char) Serial.read(); // Read in one char at a time
delay(5); // Delay for 5 ms so the next char has time to be received
}
if (input == "on")
{
digitalWrite(led, HIGH); // on
}
else if (input == "off")
{
digitalWrite(led, LOW); // off
}
}
The one problem with this script is after the serial coms close the light turns off. To fix this, I used a 10uF electrolytic capacitor between the ground and reset pin to keep the serial port open. (Please note: only put the cap in AFTER you've programmed the Arduino. If you need to reprogram, pull it out first.)
I get random characters when I perform a serial.readline(), sometimes it is other umbers and sometimes it is whole messages. The output should be "1,2" or "2,2"
Here are a couple of screenshots of the output of serial.readline().
I have tried to put a delay at before serial.readline() but it did not make a difference.
There is usually a strange character at the beginning:
I have also received strange messages:
There is a problem later on in the program that causes the program to hand because sometimes I just receive a blank line.
Is there a way to get consistent output from serial?
Here is the arduino code:
void setup() {
// initialize the LED pin as an output:
pinMode(ledPin, OUTPUT);
// initialize the pushbutton pin as an input:
pinMode(buttonPin, INPUT);
Serial.begin(9600);
}
void loop(){
// read the state of the pushbutton value:
buttonState = digitalRead(buttonPin);
// check if the pushbutton is pressed.
// if it is, the buttonState is HIGH:
if (buttonState == HIGH) {
// turn LED on:
digitalWrite(ledPin, HIGH);
Serial.println("1,2");
}
else {
// turn LED off:
digitalWrite(ledPin, LOW);
Serial.println("2,2");
}
}
And here is the python code:
ser = serial.Serial('/dev/ttyUSB0', 9600)
line=ser.readline()
coord= line.strip()
print coord
EDIT:
I tried putting ser.flushInput() after the ser.open() and I get the same output.
Have you flushed the serial buffer
ser.flushInput()
I've been having the same issue when interfacing between pyserial and Arduino. This may be an old post, but in case someone else is having the same trouble, I remedied my problem by inserting:
ser.flushInput()
...right before my ser.readline() call.
How to Read serial data from an Arduino in Linux
Nothing fancy here. I’m getting the current port configuration, setting the input/output speed to 9600 baud, setting the data expectations to be 8 bits per word, with no parity bit and no stop bit, setting canonical mode, and then committing those changes back to the serial port.
If I am not mistaken you have to change the mentioned settings when connecting through serial port.
You do not mention it, but I guess you are using the pySerial library. Regardless you can use it with the correct settings to connect through serial. The constructor of the API allows all the parameters which are noted below:
Pyserial Library
I have not tested this approach as I have had a similar problem in C not Python.
What happens if you break it down to something simpler?
On the Arduino:
void setup()
{
Serial.begin(9600);
}
int i = 0;
void loop()
{
delay(1000);
Serial.print("Hello World ");
Serial.println(i);
i++;
}
And in the Python REPL...
import serial
ser = serial.Serial('/dev/ttyUSB0', 9600)
while(1):
ser.readline()
The first behavior you should observe is that when you initiate the serial connection, the Arduino reboots. This takes a few seconds.
The second behavior you should observe is that (if you're typing this in the REPL slowly) when the while(1) loop begins, you get all of the serial data that had accumulated since you initiated the serial connection. It takes me a few seconds to type all that out, so when I hit Enter after ser.readline() I see:
'Hello World 1\r\n'
'Hello World 2\r\n'
'Hello World 3\r\n'
I only mention this to make sure you're familiar with two things that burned me a bit the last time I messed with serial communication to an Arduino: it needs time to reboot after connecting, and the serial data buffers. readline() will only give you one line in the buffer - not everything that's in the buffer.
Is it possible you're trying to sent/receive data before it's done rebooting? What about button bounce - could a dozen high/low state detections be messing something up?
I have a python code that sends in a pattern, in which a light has to blink in. (say eg. 101010. pattern may vary every time the code is run). when it is executing this infinitely i want an interrupt( again sent by the python code )to save the present conditions of the lights (say it is running 1 of the sequence) and perform a specific task like turn off the lights for 10 seconds and then resume the sequence.
one way of doing this is by interrupting the program by making the interrupt pin high. The question is can this making of high/low controlled by the pyserial.
So a simple pseudo code would be :
PYTHON part of the code:
Read the sequence:
Send the sequence to the arduino board using pyserial.
while(1)
{
Run a timer for 15 second.
When the timer overflows interrupt the arduino.
}
ARDUINO part of the code :
Read the sequence
while (1)
{
keep repeating the sequence on the LED.
}
// if interrupted on pin2 // assuming pin2 has the interrupt procedure
// Pyserial has to enable the interrupt WITHOUT using a switch for enabling the pin.
ISR
{
Save the present state of execution.
Turn off the LED.
}
FOR A BETTER UNDERSTANDING :
I built up small codes to show the doubts i had :
CODE FOR THE ARDUINO IS :
int ledpin1 = 13;
int speedy;
int patterns;
void setup()
{
Serial.begin(9600);
Serial.print("Program Initiated: \n");
pinMode(ledpin1,OUTPUT);
//activate the blackout ISR when a interrupt is achieved at a certain pin. In this case pin2 of the arduino
attachInterrupt(0,blackout,CHANGE);
}
void loop()
{
if (Serial.available()>1)
{
Serial.print("starting loop \n");
patterns = Serial.read();
patterns = patterns-48;
speedy = Serial.read();
speedy = (speedy-48)*1000;
while(1)
{
patterns = !(patterns);
Serial.print(patterns);
digitalWrite(ledpin1,patterns);
delay(speedy);
}
}
}
/*
void blackout()
{
// ***Save the present state of the LED(on pin13)***
Serial.print ("The turning off LED's for performing the python code\n");
digitalWrite(ledpin,LOW);
//wait for the Python code to complete the task it wants to perform,
//so got to dealy the completion of the ISR
delay(2000);// delay the whole thing by 2 seconds
//***Continue with the while loop by setting the condition of the light to the saved condition.***
}
*/
==================================================================================
CODE FOR THE PYTHON FRONT IS :
import serial
import time
patterns=1
speedy=1
ser = serial.Serial()
ser.setPort("COM4")
ser.baudrate = 9600
ser.open()
def main():
if (ser.isOpen()):
#while(1):
ser.write(patterns)
ser.write(speedy)
blackoutfunc()
#ser.close()
def blackoutfunc():
while(1):
time.sleep(2)
print "Performing operations as required"
===============================================================================
Now the questions I had :
1) Is there a way to be able to activate the "blackout ISR" depending on the conditions of a pin(in this case pin2 which is the INT0 pin) without using a physical switch present on the pin. Hence the pin state has to be manipulated by the software.
2) Is it possible to perform the operations as mentioned in the comments of the blackout functions?
3) In the python code is it possible to just send in the data(i.e. patterns,speedy) only once and make the arduino perform the pattern in a infinite way without again sending the data by the serial.write command. Hence avoiding the while(1) loop after the ser.isOpen().
Have a look at this:
https://github.com/ajfisher/arduino-command-server
It's something I pulled together on the Arduino side to issue arbitrary commands like switch a pin high / low and set PWM levels etc. It works over both serial and network though it's a touch buggy on the network side at the moment.
To use it, put the code on your arduino then you just write a python script (or any other language that can use a serial connection) to connect over the serial connection and then tell it what you want to do eg DIGW 1 HIGH etc
Also have a look at: https://github.com/ajfisher/arduino-django-visualiser which is where I use a variation of this library to control some LEDs based on some things going on in Django - it's more heavily python based.