danjperron
Posts: 3402
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Led post using RPI with CANOpen protocol

Tue Oct 29, 2013 3:31 am

I was thinking on replacing my lamps post which use solar. They are not working very well. The sun is not directly at them because of my tree. The day start to shorten and they don't have enough time to recharge. Also they don't last long, a couple of hour and that's it.

I decide to use wires instead. And if I use wires, I should be able to control them individually. On all the protocol available, I choose the CAN bus system for the cost and robustness. Only 4 wires will be needed. Power, ground and the can bus wires.
And This will be good for up to 127 posts on the same bus. You need more , just add an other MCP2515 bus driver.
Using a small Microchip cpu, it will be possible to minimize the cost on each post.

This is the current schematic I will used.

Image

And this is the current prototype with 2 nodes.

Image

Information about the RPI MCP2515 CAN bus driver could be found on this forum
http://www.raspberrypi.org/phpBB3/viewt ... =44&t=7027


The system is quite simple . All information is send from the Raspbery PI using CANOpen protocol which could be simplify by a standard CAN bus frame.

canbus Id = 0x300 + NodeId
canbus data length = 3
canbus data = 3 bytes of Red, Green and blue information.

P.S. Canbus Id = 0x200 +NodeId send information on each leds inside a post using pack 32 levels RGB
This is a source code in C example

Code: Select all

[email protected]:~# cat SetLedPost.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <net/if.h>

#include <linux/can.h>
#include <linux/can/raw.h>


#define AllNode 0x300

int DecodeNumber(char * _string)
{
   int value;
   if(strncmp(_string,"0x",2)==0)
     {
      if(sscanf(_string,"%X",&value)==1)
       return value;
     }
   else
     {
      if(sscanf(_string,"%d",&value)==1)
       return value;
     }
  printf("Unable to decode \"%s\"\n");
  return -1;
}


int main (int argc , char * argv[])
{
    int sfd;

    int NodeId;
    int Red,Green,Blue;
    int minV,maxV;


    if(argc != 5)
    {
     printf ("Usage:\n SetLedPost  NodeID  RED GREEN BLUE",argv[0]);
     return -1;
    }

    NodeId = DecodeNumber(argv[1]);
    if((NodeId<0) || (NodeId > 127))
    {
     printf("NodeID has to be  between 0 and 127 inclusively\nNodeId = 0 means all node");
     return -2;
    }

    Red   = DecodeNumber(argv[2]);
    Green = DecodeNumber(argv[3]);
    Blue  = DecodeNumber(argv[4]);

#define MIN(A,B)  ((A)<(B) ? (A) : (B))
#define MAX(A,B)  ((A)>(B) ? (A) : (B))


    minV = MIN(Red,MIN(Green,Blue));
    maxV = MAX(Red,MAX(Green,Blue));

    if((minV < 0) || (maxV >255))
    {
     printf("Color value has to be between 0 and 255 inclusively");
     return -3;
    }

     struct sockaddr_can addr;
     struct ifreq ifr;
     struct can_frame frame_rd;
     // open part SocketCAN
     sfd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
     if(sfd<0){
         perror("socket : ");
         return -1;
     }



  strcpy(ifr.ifr_name, "can0"); // "can0" is the name of the CAN network interface
     ioctl(sfd, SIOCGIFINDEX, &ifr);

     addr.can_family = AF_CAN;
     addr.can_ifindex = ifr.ifr_ifindex;

     // Bind the socket
     if(bind(sfd, (struct sockaddr *)&addr, sizeof(addr))<0){
         perror("bind: ");
         close(sfd);
         return -1;
        }


        printf("Set All Leds on ");
      if(NodeId==0)
        printf("All Nodes ");
      else
        printf("Node %d ", NodeId);

       printf("to  RGB(%d,%d,%d)\n",Red,Green,Blue);


     struct can_frame frame_led;
     frame_led.can_id = 0x300 + NodeId;  // use   all led 8 bit resolution TPDO output
     frame_led.can_dlc = 3;  // only 3 bytes  Red,Green and blue
     frame_led.data[0]=Red;
     frame_led.data[1]=Green;
     frame_led.data[2]=Blue;
     write(sfd,&frame_led,sizeof(struct can_frame));
     close(sfd);
     return 0;

}


To turn all posts to a RGB color value you execute the command
SetLedPost 0 255,255,0
This will colorize all led post to yellow
and if you use SetLedPost 1 128,0,0 , the Led Post corresponding to Node 1 will be red.

And this is a small video of the prototype in action https://dl.dropboxusercontent.com/s/aaw ... movie1.MP4

I'm using libcanopen to modify any sdo parameters, like the NodeID on the fly, but I can't figure out yet how to send raw PDO with it. Also Python 2.7 can't handle the can bus socket flag. Maybe I will have to create some add-on module.

I don't know is somebody is familiar with libcanopen from https://github.com/rscada/libcanopen
I can't figure out how to make the TPDO function works.
I was able to create a function to change the NodeID using SDO.

Code: Select all

[email protected]:~# cat ChangeNodeID.py 
#!/usr/bin/python
# ------------------------------------------------------------------------------
# Copyright (C) 2013, Daniel Perron
# All rights reserved.
#
# This file use libcanopen from rSCADA system.
#
# rSCADA 
# http://www.rSCADA.se
# [email protected]
# ------------------------------------------------------------------------------

# ==========
# LedPost module  change current node id 
# using  index 0x2101 sub 00 
#
#  register 0x2101,0  is hardwire to pic18f26k80 flash memory
# ==========

import sys
from pycanopen import *


canopen = CANopen()

if len(sys.argv) == 3:
    # get the current and new node 
    node = int(sys.argv[1])
    newnode = int(sys.argv[2])
else:
    print("usage: %s Current_NODE New_NODE" % sys.argv[0])
    exit(1)

canopen.SDODownloadExp(node,0x2101,0,newnode,1)

value = canopen.SDOUploadExp(node,0x2101,0)

print "ReRead ... new NodeID is {0:d}" .format(value)
print "wait 30 sec and you need to reset the node"
print "Complete but need to be verified"
If someone has knowledge about all function of libcanopen , you are welcome to comment.


Also The PIC cpu source code will be post on Github if any interest.
Daniel
Last edited by danjperron on Wed Mar 21, 2018 6:33 pm, edited 1 time in total.

danjperron
Posts: 3402
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Led post using RPI with CANOpen protocol

Wed Oct 30, 2013 12:41 am

I just add the PIC cpu source code into the github https://github.com/danjperron/LedPost


It has been compile for thePIC18F25K80. It will work for the PIC18F26K80 without any modification.

The CANBUS bit rate has to be 125KBs and the Default NodeID in the LedPost.hex file is 127.


Daniel

danjperron
Posts: 3402
Joined: Thu Dec 27, 2012 4:05 am
Location: Québec, Canada

Re: Led post using RPI with CANOpen protocol

Thu Oct 31, 2013 2:12 am

I did a small veroboard prototype and install it inside one of my solar LED post

Image

And this is the look when I change color with a simple CANBUS command.

Image


Now I will have to create the surface mount board and find 30 lamp posts. One on each of my post.


Daniel

Return to “Interfacing (DSI, CSI, I2C, etc.)”