User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

Running Python Scripts over I2C between RPi and Arduino

Tue Jun 30, 2015 2:36 pm

Hi;

I am having trouble getting my Python RPi / Arduino Nano control scripts to operate over Apache Webserver, they work fine if I run them in terminal but simply will not run when executed from a web page.

I have been successfully turning on LEDs and Motors attached to an Arduino Nano ATMega328 using a python script which send the command over an I2C interface.

The problem I know have is that these I2C commands just will not work when initiated from a web page. The web page is working because a Python script to switch on an LED connected directly to the Pi seems to work fine.

Do I need to enable something in Apache for this to work?? I wouldn't have thought so because the html / javascript to call the script seems to work?

To test the web page has two buttons, one to turn on an LED connected to GPIO23 mounted directly on the Pi and another button to turn on an LED connected to pin D02 on the Arduino Nano. The first works, the second doesn't, the code for the second does work if called from a terminal on the Pi.

Here are the various files:

Web page:

Code: Select all

<!DOCTYPE html>
<html lang="e">
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheets" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.3/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
  <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<script> 
function led13()
{
	document.location="cgi-bin/led13.py";
}
function pigpio_led()
{
	document.location="cgi-bin/pigpio_led.py";
}
</script>
</head>	
<body>
<h1>LED</h1>
<div class="container">
 <h2>Button Styles</h>
 <button type="button" class="btn btn-default" onmousedown ="led13()">LED_I2C</button> 
 <button type="button" class="btn btn-default" onmousedown = "pigpio_led()">PiGPIO</button>
</div>
</body>
</html>
Python I2C Script:

Code: Select all

#!/usr/bin/env python

import cgi
import smbus
import time
import cgitb   # Comment out when debugged.
cgitb.enable() # Comment out when debugged.

print("Status: 204 No Content")
print("Content-type: text/plain")
print("")

# RPi rev 1 = SMBus(0)
# RPi rev 2 = SMBus(1)
bus = smbus.SMBus(1)

# address of the Arduino use "i2cdetect -y 1" from the RPi prompt to detect the Arduinos (up to 127!)
device = 0x21

# initialize variables
pin      = ""  #holds the pin number 0 - 13 or A0 - A3
type     = ""  #holds the pin type: INPUT, OUTPUT, INPUT_PULLUP
mode     = ""  #holds the pinmode: HIGH, LOW, PWM
pwmValue = ""  #holds the pwmValue
pwm      = ""  #holds the pwmValue in 3 digits
val      = ""  #holds a String to be converted into ASCII
cmd      = ""  #holds the first byte of the message for the Arduino
message  = ""  #holds the second - seventh byte of the message for the Arduino
valCmd   = 88                #holds the command as ASCII value 88 = "X" 
valMessage  = [88,88,88,88,88,88] #holds the Message as ASCII values

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine sends a setPin command to the Arduino to make a pin INPUT, OUTPUT or INPUT_PULLUP
def setPin(device, pin, type):
        cmd = "S"
        message = pinString(pin)+type[0]+"000"
        sendMessage(device, cmd, message)

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine sends a writePin command to the Arduino to turn a pin HIGH or LOW
def writePin(device, pin, mode):
        cmd = "W"
        message = pinString(pin)+mode[0]+"000"
        sendMessage(device, cmd, message)

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine send an analogWritePin command to the Arduino to set a PWM pin to a duty cycle between 0 and 255
def analogWritePin(device, pin, pwmValue):
        cmd = "A"
        message = pinString(pin)+"X"+pwmString(pwm)
        sendMessage(device, cmd, message)

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine converts Strings to ASCII code
def StringToBytes(val):
        retVal = []
        for c in val:
                retVal.append(ord(c))
        return retVal


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine actually transmits the command, 
# sleep is required in order to prevent a request overload on the Arduino
def sendMessage(device, cmd, message):
        cmd=cmd.upper()
        message = message.upper()
        valCmd = ord(cmd)
        valMessage  = StringToBytes(message)
        print("Message: " + cmd + message + " send to device " + str(device))        
        bus.write_i2c_block_data(device, valCmd, valMessage) 
        time.sleep(1)


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine send a request to the Arduino to provide a 30 byte status update, return all 30 bytes
def getStatus(device):
        status = ""
        for i in range (0, 30):
            status += chr(bus.read_byte(device))
            time.sleep(0.05);
        time.sleep(0.1)        
        return status

# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine send a request to the Arduino to provide a 30 byte status update, return the value of a single pin
def pinValue(device,pin):
        status = ""
        for i in range (0, 30):
            status += chr(bus.read_byte(device))
            time.sleep(0.05);
        pinvalues = {'0':status[0],
                     '1':status[1],
                     '2':status[2],
                     '3':status[3],
                     '4':status[4],
                     '5':status[5],
                     '6':status[6],
                     '7':status[7],
                     '8':status[8],
                     '9':status[9],
                     '10':status[10],
                     '11':status[11],
                     '12':status[12],
                     '13':status[13],
                     'A0':int(status[14]+status[15]+status[16]+status[17])-1000,
                     'A1':int(status[18]+status[19]+status[20]+status[21])-1000,
                     'A2':int(status[22]+status[23]+status[24]+status[25])-1000,
                     'A3':int(status[26]+status[27]+status[28]+status[29])-1000}
        time.sleep(0.1)
        return  pinvalues[pin]


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine converts a 1 or 2 digit pin into a 2 digit equivalent
def pinString(pin):
        while len(pin) < 2:
              pin = "0"+pin;
        return pin


# ------------------------------------------------------------------------------------------------------------------------------------------------
# this routine converts a 1, 2 or 3 digit pin into a 3 digit equivalent 
def pwmString(pwm):
        while len(pwm) < 3:
              pwm = "0"+pwm;
        return pwm

        
# ------------------------------------------------------------------------------------------------------------------------------------------------
# this is where the main program starts

setPin(33, "02", "Output")  #on Arduino with I2C ID #33 set pin 04 to OUTPUT
writePin(33, "02", "High")
time.sleep(1)
# print ("Status pin 13 =" + pinValue(33,'13'))
writePin(33, "02", "Low")   #on Arduino with I2C ID #33 set pin 08 HIGH
# print ("Status pin 13 =" +pinValue(33,'13'))
 
Arduino Sketch Script to set the Arduino as a Slave and listen for commands:

Code: Select all

// Arduino I2C Wire Slave version 0.21
// by Racer993 <http://raspberrypi4dummies.wordpress.com/>

// Turns the Arduino to a I2C slave device (for the Raspberry Pi) 
// using the Wire library. Configure pins, read and write to pins
// via a simple instruction set.

// Supported instructions
// pinMode = setPin(device, pinnumber, INPUT/OUTPUT/INPUT_PULLUP)
// digitalWrite = writePin(device,pinnumber,HIGH/LOW)
// analogWrite = analogWritePin(device,pinnumber,0-255)
// getStatus(device)

// A0 - analog read / digital write
// A1 - analog read / digital write
// A2 - analog read / digital write
// A3 - analog read / digital write
// A4 - IN USE as SDA
// A5 - IN USE as SCL

//  1 - digital read / write + RX
//  2 - digital read / write + TX  + Interrupt
//  3 - digital read / write + PWM + Interrupt
//  4 - digital read / write
//  5 - digital read / write + PWM
//  6 - digital read / write + PWM
//  7 - digital read / write
//  8 - digital read / write
//  9 - digital read / write + PWM
// 10 - digital read / write + PWM + SPI - SS
// 11 - digital read / write + PWM + SPI - MOSI
// 12 - digital read / write +       SPI - MISO
// 13 - digital read / write + LED + SPI - SCK

// HOW TO USE

// sending commands

// general: all commands must be 7 bytes long + 1 ending byte

// 1) to set the pinMode write a message with 7 characters on I2C bus to the arduino
// first character = S for set pinMode
// second & third character are pin ID 00 - 13 for digital pins & A0 - A3 for analog pins
// fourth character is to set the mode I for INPUT, O for OUTPUT, P for INPUT_PULLUP
// character 5,6,7 are not used, set to 000

// 2) to turn the pin on or off write a message with 7 characters on I2C bus to the arduino
// first character = W for digitalWrite
// second & third character are pin ID 00 - 13 for digital pins & A0 - A3 for analog pins
// fourth character is to turn off or on H for HIGH and L for LOW
// character 5,6,7 are not used, set to 000

// 3) to turn use PWM write a message with 7 characters on I2C bus to the arduino
// first character = A for analogWrite
// second & third character are pin ID 00 - 13 for digital pins & A0 - A3 for analog pins
// forth character is not used, set to X
// fifth - seventh character are used to write the PWM cycle (000-255)

// 4) to get a status with pin readings send Wire.requestFrom(device, #chars = 30)
// the arduino will send back 30 chars 
// char 1-14 for each digital pin 1 = on 0 = off
// char 15-18 for reading of A0, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading
// char 19-22 for reading of A1, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading
// char 23-26 for reading of A2, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading
// char 27-30 for reading of A3, 1000 is added to the A0 reading in order to guarantee a 4 digit reading, subtract 1000 to get the proper reading

// Created 17 July 2013

// This example code is in the public domain.

#include <Wire.h>

void setup()
{
  int arduinoI2CAddress = 33;     // set the slave address for the Arduino on the I2C buss
    
  Wire.begin(arduinoI2CAddress); // join i2c bus with specified address
  Wire.onRequest(requestEvent);  // register wire.request interrupt event
  Wire.onReceive(receiveEvent);  // register wire.write interrupt event
}

  char sendStatus[31] = "000000000000000000000000000000";  // initialize the container variable
  int index = 0;                                           // initialize the index variable
  char pwm[15] = "00000000000000";                         // initialize the PWM flag container

void loop()
{
  
  String pinStatus="";                                      // initialize pinStatus variable
    
  for(int digitalPin = 0; digitalPin <= 13; digitalPin++)   // loop through 14 digital pins 0 - 13
     {
      if (pwm[digitalPin] == 0)                            // in case PWM is off for the pin, read the pin status
         {
           pinStatus += String (digitalRead(digitalPin));   // read the pin status & add it to the container variable
         }
      else
         { 
           pinStatus += "P";                                // in case PWM is on for the pin, add P to the pin status container string
         } 
     }
  
  for(int analogPin = 0; analogPin <= 3; analogPin++)      // loop through the 4 (unused) analog pins 0 - 3
     {
      pinStatus += String (1000+analogRead(analogPin));    // read the analog value from the pin, add 1000 to make it 4 digit & add it to the container variable
     }  
  
   pinStatus.toCharArray(sendStatus, 31);                  // convert the container variable pinStatus to a char array which can be send over i2c
    
  delay(1000);                                             // wait for an interrupt event
}

//--------------------------------------------------------------------------------
// function that executes whenever a status update is requested by master
// this function is registered as an event, see setup()

void requestEvent() { 
   Wire.write(sendStatus[index]);
    ++index;
    if (index >= 30) {
         index = 0;
    }
 }

//--------------------------------------------------------------------------------
// function that executes whenever a message is received from master
// this function is registered as an event, see setup()

void receiveEvent(int howMany)
{
  int receiveByte = 0;                   // set index to 0
  char command[7];                       // expect 7 char + 1 end byte
  String mode = "";                      // initialize mode variable for holding the mode
  String pin = "";                       // initialize pin variable for holding the pin number as a String
  String awValue = "";                   // intitalize the variable for holding the analogWrite value
  int pinVal;                            // inititalize the variable for holding the pin number as integer
  int awValueVal;                        // initialize the variable for holding the analog write value as integer (only PWM pins!)   
    
  while(Wire.available())                // loop through all incoming bytes
  {
    command[receiveByte] = Wire.read();  // receive byte as a character
    receiveByte++;                       // increase index by 1
  }
  
  pin = String(command[1]) + String(command[2]);                          // combine byte 2 and 3 in order to get the pin number
  awValue = String(command[4]) + String(command[5]) + String(command[6]); // combine byte 5, 6 and 7 in order to get the analogWrite value
  awValueVal = awValue.toInt();                                           // convert the awValue string to a value
  
  if (String(command[1]) != "A" ) { pinVal = pin.toInt();}                // in case of not an analog pin assignment convert into digital pin number
  if (String(command[1]) != "A" ) { pwm[pinVal] = 0;}                     // in case of not an analog pin assignment set PWM flag to 0

// incase of analog pin assignment determine analog pin to be set  
  if (String(command[1]) == "A" && String(command[2]) == "0") { pinVal = A0;}
  if (String(command[1]) == "A" && String(command[2]) == "1") { pinVal = A1;}
  if (String(command[1]) == "A" && String(command[2]) == "2") { pinVal = A2;}
  if (String(command[1]) == "A" && String(command[2]) == "3") { pinVal = A3;}

// if requested set pinmode  
  if (String(command[0]) == "S" && String(command[3]) == "I") { pinMode(pinVal, INPUT);}
  if (String(command[0]) == "S" && String(command[3]) == "O") { pinMode(pinVal, OUTPUT);}
  if (String(command[0]) == "S" && String(command[3]) == "P") { pinMode(pinVal, INPUT_PULLUP);}

// if requested perform digital write
  if (String(command[0]) == "W" && String(command[3]) == "H") { digitalWrite(pinVal, HIGH);}
  if (String(command[0]) == "W" && String(command[3]) == "L") { digitalWrite(pinVal, LOW);}


// if requested perform analog write
  if (String(command[0]) == "A" && pinVal == 3 || pinVal == 5 || pinVal == 6 || pinVal == 9 || pinVal == 10 || pinVal == 11 ) 
      { 
       analogWrite(pinVal, awValueVal);
       pwm[pinVal] = 1;
      }
  
}
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

User avatar
DougieLawson
Posts: 39297
Joined: Sun Jun 16, 2013 11:19 pm
Location: A small cave in deepest darkest Basingstoke, UK
Contact: Website Twitter

Re: Running Python Scripts over I2C between RPi and Arduino

Tue Jun 30, 2015 3:28 pm

sudo adduser www-data i2c
sudo /etc/init.d/apache2 stop # or lightttpd or nginx if you run either of those.
sudo /etc/init.d/apache2 start
Note: Any requirement to use a crystal ball or mind reading will result in me ignoring your question.

Criticising any questions is banned on this forum.

Any DMs sent on Twitter will be answered next month.
All non-medical doctors are on my foes list.

User avatar
JonnyAlpha
Raspberry Pi Certified Educator
Raspberry Pi Certified Educator
Posts: 572
Joined: Sat Nov 02, 2013 2:06 pm

Re: Running Python Scripts over I2C between RPi and Arduino

Tue Jun 30, 2015 4:36 pm

Dougie;

A million thank yous, just did exactly as you posted and hey presto my I2C Arduino connected LED just fired up.

Absolutley brilliant!!

Amazing how excited you can get about a 5mm LED :-)
Raspberry Pi Certified Educator. Main Hardware - Raspberry Pi 1 model B revision 2, Raspberry Pi 2 model B, Pi Camera

Return to “General programming discussion”