I'm trying to get an ATMEGA 324PA to run as an SMBus slave.
I'm using the following code on the Pi:
import smbus as smbus
i2c = smbus.SMBus(1)
i2c_addr = 0x30
result = i2c.read_block_data( i2c_addr, reg )
On the AVR, I'm using:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "smb_slave.h"
#define SMB_COMMAND_RETURN_VENDOR_STRING 0x10
int main(void)
{
// Set data direction of PORTB as output and turn off LEDs.
DDRA = 0xff;
PORTA = 0xff;
// Initialize SMBus
SMBusInit();
SMBEnable();
// Enable interrupts globally
sei();
for (;;)
{
}
return 0;
}
void ProcessReceiveByte(SMBData *smb)
{
smb->txBuffer[0] = ~PIND;
smb->txLength = 1;
}
static void ReturnVendorString(SMBData *smb)
{
unsigned char *vendor = (unsigned char*) "Vendor\0";
unsigned char i;
unsigned char temp;
i = 0;
// Copy vendor ID string from EEPROM.
while ((temp = vendor[i]) != '\0')
{
i++;
smb->txBuffer[i] = temp;
}
smb->txBuffer[0] = i; // Byte count.
smb->txLength = i + 1; // Number of bytes to be transmitted including byte count.
smb->state = SMB_STATE_WRITE_READ_REQUESTED;
PORTA ^= 0x40; // debug
}
static void UndefinedCommand(SMBData *smb)
{
// Handle undefined requests here.
smb->error = TRUE;
smb->state = SMB_STATE_IDLE;
}
void ProcessMessage(SMBData *smb)
{
if (smb->state == SMB_STATE_WRITE_REQUESTED)
{
switch (smb->rxBuffer[0]) // Command code.
{
case SMB_COMMAND_RETURN_VENDOR_STRING: // Block read, vendor ID.
ReturnVendorString(smb);
break;
default:
UndefinedCommand(smb);
break;
}
}
else
{
smb->state = SMB_STATE_IDLE;
}
}
With a (gcc-adapted) version of: http://www.atmel.com/images/AVR316.zip from http://www.atmel.com/devices/ATMEGA324A.aspx?tab=documents
Something's partially working, as my logic analyser shows:
But I assume I'm doing something wrong as the AVR is not ACK'ing the READ, nor clock-stretching nor sending a response.
Where should I look next?
Can I have confidence in the Python smbus module on the RasPi?
Could what I'm seeing be related to https://github.com/raspberrypi/linux/issues/254 ?
The issue you linked is the problem -- i2c clock stretching is simply broken on the Raspberry Pi. More info: http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
If a sensor has alternative output such as UART sometimes that's an option but for some projects I've had to use a micro or Beaglebone or some other thing.
I tried using SMBus from a Beaglebone instead (replacing the Raspberry Pi).
This worked perfectly, after I added some 10K pull-up resistors to the i2c bus. (The Raspberry Pi has internal pull-ups on the i2c pins.)
Related
i use an MCP9600 sensor to know the temperature. I use an ATmega 2560 and i communicate from my laptop with python with the library serial.
this is my arduino
#include <Wire.h>
#include <Adafruit_I2CDevice.h>
#include <Adafruit_I2CRegister.h>
#include "Adafruit_MCP9600.h"
#include <SimpleCLI.h>
#define I2C_ADDRESS (0x67)
Adafruit_MCP9600 mcp;
uint8_t filtcoeff;
// Create CLI Object
SimpleCLI cli;
// Commands
Command type;
Command filter;
Command temp;
Command fanspeed;
Command Tmax;
Command tolerance;
//Call function
void overheat();
void deltatemp();
// Callback function
void typeCallback();
void filterCallback();
void temperatureCallback();
void FanCallback();
void overheatCallback();
void toleranceCallback();
int broche_PWM = 2; // Déclaration de la Pin 10 Arduino
int Valeur_PWM = 128; // Variable
int timedelay = 300;
int tempTolerance = 10;
int tempmax = 50;
void setup() {
// put your setup code here, to run once:
pinMode(broche_PWM, OUTPUT); // Pin 2 Arduino en sortie PWM
Serial.begin(115200);
while (!Serial) {
delay(10);
}
cli.setOnError(errorCallback); // Set error Callback
/* Initialise the driver with I2C_ADDRESS and the default I2C bus. */
if (! mcp.begin(I2C_ADDRESS)) {
Serial.println("Sensor not found. Check wiring!");
while (1);
}
mcp.setADCresolution(MCP9600_ADCRESOLUTION_12);
switch (mcp.getADCresolution()) {
case MCP9600_ADCRESOLUTION_18: ; break;
case MCP9600_ADCRESOLUTION_16: ; break;
case MCP9600_ADCRESOLUTION_14: ; break;
case MCP9600_ADCRESOLUTION_12: ; break;
}
mcp.setThermocoupleType(MCP9600_TYPE_K);
switch (mcp.getThermocoupleType()) {
case MCP9600_TYPE_K: ; break;
case MCP9600_TYPE_J: ; break;
case MCP9600_TYPE_T: ; break;
case MCP9600_TYPE_N: ; break;
case MCP9600_TYPE_S: ; break;
case MCP9600_TYPE_E: ; break;
case MCP9600_TYPE_B: ; break;
case MCP9600_TYPE_R: ; break;
}
mcp.setFilterCoefficient(3);
mcp.setAlertTemperature(1, 30);
mcp.configureAlert(1, true, true); // alert 1 enabled, rising temp
mcp.enable(true);
type = cli.addBoundlessCommand("type", typeCallback);
filter = cli.addBoundlessCommand("filter", filterCallback);
temp = cli.addBoundlessCommand("temp", temperatureCallback);
fanspeed = cli.addBoundlessCommand("fanspeed", FanCallback);
Tmax = cli.addBoundlessCommand("Tmax", overheatCallback);
tolerance = cli.addBoundlessCommand("Tolerance", toleranceCallback);
Serial.println("I am ready"); // avoid to have a void monitor (avoiding blocking in python code)
}
void loop() {
// put your main code here, to run repeatedly:
// Check if user typed something into the serial monitor
if (Serial.available()) {
// Read out string from the serial monitor
String input = Serial.readStringUntil('\n');
// Parse the user input into the CLI
cli.parse(input);
}
if (cli.errored()) {
CommandError cmdError = cli.getError();
Serial.print("ERROR: ");
Serial.println(cmdError.toString());
if (cmdError.hasCommand()) {
Serial.print("Did you mean \"");
Serial.print(cmdError.getCommand().toString());
Serial.println("\"?");
}
}
if (mcp.readThermocouple() > tempmax){
overheat();
}
if (abs(mcp.readThermocouple()-mcp.readAmbient())> tempTolerance){
//deltatemp();
}
analogWrite(broche_PWM,Valeur_PWM); // Envoi du signal PWM sur la sortie numérique 10
delay(timedelay);
}
void temperatureCallback(cmd* c){
Command cmd(c); // Create wrapper object
Serial.print("Hot Junction: ");
Serial.print("x");
Serial.print(mcp.readThermocouple());
Serial.print("x");
Serial.print("Cold Junction: ");
Serial.print("x");
Serial.print(mcp.readAmbient());
Serial.print("x");
Serial.print("ADC (uV): ");
Serial.print("x");
Serial.println(mcp.readADC() * 2);
}
void filterCallback(cmd* c){
Command cmd(c); // Create wrapper object
Argument arg;
String argValue = "";
int i=0;
int IntValue;
arg = cmd.getArg(i);
argValue = arg.getValue();
IntValue = argValue.toInt();
mcp.setFilterCoefficient(IntValue);
Serial.print("Filter coefficient value set to: ");
Serial.println(mcp.getFilterCoefficient());
}
}
and the python code, i use to extract the data from the ATmega
import numpy as np
import serial
import time
def get_temperature(serial_port):
phrase = b'temp'
# print("str.encode(phrase)",str.encode(phrase))
serial_port.write(phrase)
exitloop = 0
while exitloop == 0:
if serial_port.inWaiting() > 0:
arduino_data = serial_port.readline()
print(arduino_data)
serial_port.flushOutput()
exitloop = 1
serial_port.flushOutput()
serial_port.flushInput()
return arduino_data
the thermocouple is a K type. the problem is that the value coming from the sensor give constant during a long time when i see from the arduino serial monitor and from my python code. Normally, the value must change constantly when i compare with another device but give the same value a long time like 5 min.
Someone can give a solution?
I found the problem. It comes from the ADC_resolution, i gave back at 18.
Thanks
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=""
I'm having a project to use pyserial to send a number to an Arduino. This number is RPM (round per minute). Arduino will receive this number to control the motor (in here I use PID Controller) and then send the number Arduino received back to Python console. The problem is, I tried to send float number to Arduino, but it receives nothing. Here is the problem:
#include <TimerOne.h>
#include <Encoder.h>
#define A 2 //Encoder pins
#define B 3
Encoder encoder(2,3);
//Config pins for motor control:
int pinAIN1 = 9; //Direction
int pinAIN2 = 8; //Direction
int pinPWMA = 5; //Speed
int pinSTBY = 4; //Stanby
float receive;
float tsamp = 0.01;
float xung = 0;
float last_xung = 0;
float current_speed = 0;
float ref_speed ; //The reference speed
float error = 0;
float last_error = 0;
float PID,last_PID;
float Kp =0.15 , Ki =0, Kd = 0.01;
void dotocdo()
{
if ( ref_speed >= 0)
{
digitalWrite(pinAIN1, 1);
digitalWrite(pinAIN2, 0);
analogWrite(pinPWMA, PID);
}
if ( ref_speed < 0)
{
digitalWrite(pinAIN1, 0);
digitalWrite(pinAIN2, 1);
analogWrite(pinPWMA, abs(PID));
}
float P;
static float I,D;
current_speed = 60*(xung-last_xung)/(tsamp*374*4);
last_xung = xung;
Serial.println(current_speed);
error=abs(ref_speed)-abs(current_speed);
P = Kp*error;
D = Kd*(error - last_error)/tsamp;
I = Ki*error*tsamp;
PID = last_PID + P + I + D;
last_error = error;
last_PID = PID;
if (PID >= 255) {PID = 255;}
if (PID <= -255) {PID = -255;}
}
void setup() {
Serial.begin(115200);
pinMode(pinPWMA, OUTPUT);
pinMode(pinAIN1, OUTPUT);
pinMode(pinAIN2, OUTPUT);
pinMode(pinSTBY, OUTPUT);
pinMode(A, INPUT);
pinMode(B, INPUT);
digitalWrite(pinSTBY, 1);
Timer1.initialize(10000);
Timer1.attachInterrupt(dotocdo);
}
void loop()
{
if (Serial.available())
{
receive= Serial.parseFloat();
ref_speed=receive;
Serial.println(receive);
}
xung=encoder.read();
}
And here is the Python code on Raspberry:
import time
import socket
import sys
import serial
import struct
UNO_1 = serial.Serial('/dev/ttyUSB0', 115200)
while (1):
n=float(25)
UNO_1.write(bytes(b'%f'%n))
receive=UNO_1.readline()
print(receive)
This is the error (Arduino receives nothing):
Does anyone know how to fix this problem?
Has any communication worked before?
Double-check your connections (swapped TX and RX, forgot GND)
Try using a serial terminal (I think pyserial has a demo included) to send data manually.
Your Python script may just be timing out.
Your Python script might be sending too many zeroes or control characters that Serial.parseFloat() does not like (it stops if it does not like something).
Alternativley, just get started with echo programs that don't actually try to parse numbers to see if any data comes through: try this.
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.
I am trying to implement a UDP communication protocol between a C program and a python program. The C program has a structure that it sends through the UDP port (tx_port) as binary data. This program also listens on another port (rx_port) for any received data, and then prints the received binary output to the screen.
The python program listens on tx_port and unpacks the received data and prints it to the screen. Then it repacks the data and sends it back through UDP port (rx_port).
Here are the C and Python programs that I used.
C program
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <pthread.h>
#define BUFLEN 4096
#define RX_PORT 8888
#define TX_PORT 8889
// Structure data
struct data {
long frame_number;
double time;
} tx_data, rx_data;
int dlen = sizeof(tx_data);
struct sockaddr_in si_me, si_other;
int tx_soc;
int slen = sizeof(si_other);
int recv_len;
char* buf;
pthread_t rx_thread;
void* receiver_thread(void *arg)
{
int i =0;
while (1) {
recv_len = recvfrom(tx_soc, buf, sizeof(rx_data), 0, (struct sockaddr *) &si_other, &slen);
printf("\nReceived data : %d\n", recv_len);
for (i = 0; i < recv_len; i++) {
printf("%x ", buf[i]);
}
printf("\n");
fflush(stdout);
};
}
void data_init(void) {
tx_data.frame_number = 0;
tx_data.time = 0;
};
int main(void)
{
// Initialize data
data_init();
//create a UDP socket
if ((tx_soc=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
printf("Socket error!");
exit(0);
}
// zero out the structure
memset((char *) &si_me, 0, sizeof(si_other));
memset((char *) &si_other, 0, sizeof(si_other));
// Host socket address
si_me.sin_family = AF_INET;
si_me.sin_port = htons(RX_PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
// Remote socket address
si_other.sin_family = AF_INET;
si_other.sin_port = htons(TX_PORT);
si_other.sin_addr.s_addr = htonl(INADDR_ANY);
//bind sockets to the ports
if( bind(tx_soc, (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
{
printf("Binding error!");
}
// Start reader thread.
if (pthread_create(&rx_thread, NULL, &receiver_thread, NULL) != 0) {
printf("\ncan't create thread");
}
//keep listening for data
while(1)
{
// Allocate memory for receive buffer.
buf = (char*) malloc(sizeof(rx_data));
// Update data value.
tx_data.frame_number++;
printf("\nFrame numner: %ld", tx_data.frame_number);
fflush(stdout);
// Send data.
if (sendto(tx_soc, (char*)&tx_data, dlen, 0, (struct sockaddr*) &si_other, slen) == -1)
{
printf("Sending error!");
}
sleep(1);
}
close(tx_soc);
return 0;
}
Python program
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
import struct
# Packet format string
packet_fmt = ''.join(['i', # Frame number
'd', # Frame time stamp
])
s = struct.Struct(packet_fmt)
class Echo(DatagramProtocol):
def datagramReceived(self, data, (host, port)):
new_data = s.unpack(data)
print new_data
echo_data = s.pack(*new_data)
self.transport.write(echo_data, (host, port))
reactor.listenUDP(8889, Echo())
reactor.run()
When I execute the two programs, I am able to receive data on both sides. I am able to unpack data in python, print it, repack and send it.
But on the C side, the received data does not match the sent data. I have checked on the python side to make sure the repacked data matches the original data.
Here is a sample output from the C and Python programs. I started the python programs first, and then the C program.
What is the mistake I might be making?