Jump to content

Universal Ripple Control Receiver

Recommended Posts

The goal of my project is to build ripple control receiver with some additional functions, main request was to receive massages and sent them via UART but current plan includes many more functions:

- receive and decode messages

- 4 relays triggered by certain message content

- message logging to EEPROM with date and time from RTC

- configuration will be stored on RTC DS1338 user memory

- simple CLI via UART

- 4 buttons and 20x4 character LCD interface

- listing recorded messages with filtering


Ripple control messages are transmitted to electricity distribution network, usually at 110kV or 22kV level and pass transformers down to end user 230V mains line. It is used to signal low and high tariff to electricity meters and high non-critical loads to optimize power distribution. The signal uses low modulation frequency, most common in Czech Republic is 216.7Hz. One message takes about 63 seconds to transmit. This allows one transmitter to cover large area. So far I'm getting around 200 messages a day with first receiver prototype.


I plan to use these components:

- MSP430G2553

- modified old receiver ZPA FMX-100 for signal extraction from mains, opto-coupled connection

- AT24C512 I2C EEPROM, 64Kx8 bit

- DS1338 I2C RTC

- 20x4 character LCD HD44780 with shift register 74HCT164

- ULN2803 for relay switching with shift register 74HC595

- USB to UART module with FTDI or CP2102 chip, opto-coupled UART


Does someone have good experience with DS1338 or would you recommend other RTC? I need 3.3V operation and battery backup, square wave output is welcomed.

Link to post
Share on other sites
  • 2 weeks later...

So far I have built two versions of simple message logger based on LaunchPad, first uses almost unmodified FMX-100 and the next one dropped top board and required some wire cutting on bottom board. I needed to use capacitor to smoothen the signal, 10uF with 2kOhm resistor to LED and opto coupler works just fine. So far I have around 100 hours recorded. Now it's time to concentrate on software part.


Old receiver ZPA FMX-100:




Message format, time is in secconds:


Message recorded on scope:


Logger V1:


Logger V2:


Receiver schema V0.8:


Receiver board V0.8:


Log sample, time is counted from system start just to get picture how often messages come:

A1000B00000100P___0____________ +314:11 m:s
A1000B01000000P_____1__________ +320:9 m:s
A1001B10000000P___1____1_____1_ +324:12 m:s
A1000B00110010P_______1_1___1__ +325:32 m:s
A1000B00000100P_________0______ +326:52 m:s
A0001B11000000P_______1_1___1__ +328:11 m:s
A0010B10000000P______00________ +336:9 m:s
A1010B11000100P00____00____0__0 +337:28 m:s
A1000B00100000P____000_________ +338:47 m:s
A1000B00001001P_0___010________ +340:8 m:s
A1000B00010000P____0_______0__0 +341:27 m:s

Link to post
Share on other sites

Like the way you have documented your project.


Ripple control messages are transmitted to electricity distribution network, usually at 110kV or 22kV level and pass transformers down to end user 230V mains line. It is used to signal low and high tariff to electricity meters and high non-critical loads to optimize power distribution.

I never knew this existed. How are you going to use it?

Link to post
Share on other sites

I have it assigned as a final project for my master degree. My tutor offered me couple projects and I picked this one. Before that I also didn't know this technology existed. It isn't used in my home city but it covers most of the Czech Republic. My tutor wishes to monitor the messages with simple in-device filtering and have few output relays programmable to certain addresses and behavior. It may also be used for some lab exercise like on Department of Electrical Power Engineering.

Link to post
Share on other sites
  • 4 weeks later...

I'm using I2C in my project with USCI. Here is code for write and read with repeated start, 8 or 16 bit EEPROM data address is possible. It was inspired by Anderson's code. May be useful for someone else.



#ifndef I2C_H_
#define I2C_H_

typedef unsigned char u8;
typedef signed char s8;
typedef unsigned int u16;
typedef signed int s16;

typedef struct {
   u8 count;
   u8 state;
   u16 address;
   u8 *buffer;
} i2c_data_t;

#define size_tr(size, addr16) ((size-1)<<1)|(addr16 & 0x01)

// number of bytes to transfer(1 to 128, 2+ for RX), device ID, buffer pointer, is 16bit addressing, memory address
#define i2c_tx(size, id, buffer, addr16, address) i2c_trans(size_tr(size,addr16), id, buffer, address)
#define i2c_rx(size, id, buffer, addr16, address) i2c_trans(size_tr(size,addr16), id|0x01, buffer, address)

// number of bytes to transfer|16 bit addressing, device ID|dirrection, buffer pointer, memory address  
void i2c_trans(u8 size, u8 id, u8 buffer[], u16 address);

void i2c_init(void); // setup
u8 i2c_int(void); // data transfer interrupt
u8 i2c_eint(void); // NACK interrupt

#endif /*I2C_H_*/



#include "msp430g2553.h"
#include "i2c.h"

// i2c states
#define WRITE	0x00
#define READ	0x01
#define ADDR16	0x02
#define ADDRTR	0x04
#define REPSTT	0x08

i2c_data_t i2c_data = {0, 0, 0, 0};

void i2c_init(void) {
   P1SEL |= BIT6 + BIT7; //Set I2C pins
   P1SEL2 |= BIT6 + BIT7;
   UCB0CTL1 |= UCSWRST; //Enable SW reset
   UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; //I2C Master, synchronous mode
   UCB0CTL1 = UCSSEL_2 + UCSWRST; //Use SMCLK, keep SW reset
   UCB0BR0 = 12; //fSCL = SMCLK/12 = ~100kHz
   UCB0BR1 = 0;
   UCB0CTL1 &= ~UCSWRST; //Clear SW reset, resume operation
   IE2 |= UCB0TXIE | UCB0RXIE; //Enable TX, RX interrupt
   UCB0I2CIE = UCNACKIE; // NACK interrups

void i2c_trans(u8 size, u8 id, u8 *buffer, u16 address) {
   i2c_data.state = (id & READ) | ADDRTR; // byte counter
   if (size & 0x01) i2c_data.state |= ADDR16;
   i2c_data.count = (size >> 1) + 1; // byte counter
   i2c_data.address = address; // byte counter
   i2c_data.buffer = buffer; // byte counter
   UCB0I2CSA = id >> 1; // Slave Address 01101000 0x68 RTC //Slave Address 01010000 0x50 EEPROM
   UCB0CTL1 |= UCTR + UCTXSTT; // I2C TX, start condition
   __bis_SR_register(CPUOFF + GIE); // Enter LPM0 w/ interrupts
   // Remain in LPM0 until all data is transferred

u8 i2c_int(void) {
   if (i2c_data.state == WRITE) {
       if (i2c_data.count > 0) { //Check TX byte counter
           UCB0TXBUF = *i2c_data.buffer++; // Load TX buffer
           i2c_data.count--; //Decrement TX byte counter
       } else if (i2c_data.count == 0) { //last byte transferred
           UCB0CTL1 |= UCTXSTP; //I2C stop condition
           while (UCB0CTL1 & UCTXSTP); //Ensure stop condition got sent
           IFG2 &= ~UCB0TXIFG; //Clear USCI_B0 TX int flag
           return 1; //Exit LPM0
   } else if (i2c_data.state == READ) {
       *i2c_data.buffer++ = UCB0RXBUF;
       i2c_data.count--; //Decrement RX byte counter
       if (i2c_data.count == 1) { //Check RX byte counter, 1 byte remaining
           UCB0CTL1 |= UCTXSTP; // I2C stop condition
       if (i2c_data.count == 0) { //Check RX byte counter, last byte received
           while (UCB0CTL1 & UCTXSTP); // Ensure stop condition got sent
           return 1; // Exit LPM0
   } else if (i2c_data.state & ADDR16) { // high byte address transmit
       UCB0TXBUF = _swap_bytes(i2c_data.address);
       i2c_data.state &= ~ADDR16;
   } else if (i2c_data.state & ADDRTR) { // low byte address transmit
       UCB0TXBUF = i2c_data.address;
       i2c_data.state &= ~ADDRTR;
       if (i2c_data.state) { // repeated Start for RX
           i2c_data.state |= REPSTT;
   } else if (i2c_data.state & REPSTT) { //  repeated start required
       i2c_data.state &= ~REPSTT;
       UCB0CTL1 &= ~UCTR; // I2C RX
       UCB0CTL1 |= UCTXSTT; // I2C repeated Start condition for RX
       IFG2 &= ~UCB0TXIFG; //Clear USCI_B0 TX int flag
   return 0;

u8 i2c_eint(void) {
   if (UCB0STAT & UCNACKIFG) { // send STOP if slave sends NACK
       UCB0CTL1 |= UCTXSTP;
       return 1;
   return 0;


example main.c

#include "msp430g2553.h"
#include "i2c.h"

u8 txdata[] = {'H', 'E', 'L', 'L', 'O', ' ', 'W', 'O', 'R', 'L', 'D'};
u8 rxdata[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

void main(void) {

   BCSCTL1 = CALBC1_1MHZ; //Set DCO to 1MHz

   // UART setup
   P1SEL = BIT1 + BIT2; //Set RXD and TXD
   P1SEL2 = BIT1 + BIT2;
   UCA0CTL1 |= UCSSEL_2 | UCSWRST; //Have USCI use SMCLK AKA 1MHz main CLK
   UCA0BR0 = 104; //Baud = 9600
   UCA0BR1 = 0;
   UCA0MCTL = UCBRS_1; //Modulation
   UCA0CTL1 &= ~UCSWRST; //Start USCI

   i2c_init(); // init I2C
   __delay_cycles(20000); //Just a start up delay

   i2c_rx(11, 0xA0, rxdata, 0, 0); //i2c RX data

   u8 i = 0;
   while (i < 11) {
       while (!(IFG2 & UCA0TXIFG));
       UCA0TXBUF = rxdata[i]; //UART TX data
   i2c_tx(11, 0xA0, txdata, 0, 0); //i2c TX 11 bytes("HELLO WORLD")
   __delay_cycles(20000); //Allow EEPROM to write data

   i2c_rx(11, 0xA0, rxdata, 0, 0); //i2c RX data

   i = 0;
   while (i < 11) {
       while (!(IFG2 & UCA0TXIFG));
       UCA0TXBUF = rxdata[i]; //UART TX data
   __bis_SR_register(CPUOFF + GIE); //Wait for a reset

// I2C data transfer vector
#pragma vector = USCIAB0TX_VECTOR

__interrupt void USCIAB0TX_ISR(void) {
   if (i2c_int()) __bic_SR_register_on_exit(CPUOFF); //Exit LPM0;

// I2C status vector
#pragma vector = USCIAB0RX_VECTOR

__interrupt void USCIAB0RX_ISR(void) {
   if (i2c_eint()) {
       while (!(IFG2 & UCA0TXIFG)); // send error via UART
       UCA0TXBUF = '#';

Link to post
Share on other sites
  • 1 month later...
  • 3 months later...



Thank you so much for sharing.

I`m going to use i2c interface with an EEprom memory chip from microchip (24lc256).

I used your code (I`m using CCS) for communication.


but when I debug the program it will stuck before ( u8 i2c_int(void) { ) in i2c.c file and doesn`t do any thing after that.


first time it goes to that and after going back to interrupt and coming back to i2c.c it stuck at this point.


what is the problem?

Link to post
Share on other sites

Have you just downloaded and compiled the example code or have you done any modification? The interrupt service routine must contain the if clause to correctly wake up processor after I2C transaction is done or you have to use different approach not to overlap 2 transactions.

Link to post
Share on other sites

Thanks man for response,

I used exactly your code . without any change. even I removed any other things and just tried to send and receive a sample data.


you know, when it wants to start communication it seems it`s hanging. why hanging? I can`t understand.

I`m sure it`s not IC problem . I removed P1.6 jumper and the same GND for both IC and 10K resistor pullup for SDA pin.


when I`m in debug mode and check the program line by line, when it comes back from interrupt routing and wants to start reading for the first time, it hangs.


any idea?

Link to post
Share on other sites

First thing that pops my mind is to set 16bit addressing bit because your EEPROM chip uses it.

i2c_rx(11, 0xA0, rxdata, 0, 0);
i2c_rx(11, 0xA0, rxdata, 1, 0);

In test example I was using small EEPROM that uses only 8bit addressing, STMicro 24C16. I think all devices up to 256B and some up to 2KB (from outside they behave like 8 I2C devices in one pack) use it. In final design I used 64KB chip that requires 16bit addressing.


You may also check if WP, A0, A1 and A2 are connected to GND. Another option is to place break point into I2C status interrupt that is hit when slave sends NACK or does not respond at all. Best way is to have scope or logic analyzer to see the waveform. You can watch transaction progress if you place break point at the beginning of "u8 i2c_int(void)" in i2c.c.

Link to post
Share on other sites

nothing yet.


I removed interrupts and used while loop for sending data . now it`s working. but there is another problem.


now it goes to i2c_int() . also it goes to write routine or read routine correctly. but after i2c_data.count==0 it will stay in :

while (UCB0CTL1 & UCTXSTP);

and can not pass this step.

I don`t have osiloscope around here now. do you think this problem is because of no response from EEprom memory?


I changed the code like this:


i2c_tx(11, 0xA0, txdata, 1, 0); //i2c TX 11 bytes("HELLO WORLD")

while (!(i2c_int())){




and also I removed __bis_SR_register(CPUOFF + GIE); from i2c.c

Link to post
Share on other sites

I'm not sure how functional it may be without interrupts as its written to depend on them and as is described in MSP430 2xxx User's Guide. You would need to check some ready/busy flags of USCI_B module.


However if you get to tx function you can verify functionality:

Run upto tx count=0


Run to rx count=0 and examine the memory in debug mode

rxdata buffer should contain same data as txdata


You can also test Anderson's code for I2C USCI, it is for 16bit addressing.

Link to post
Share on other sites
  • 8 months later...

I have all the data stored at http://sunny.ok.cvut.cz/~vojta/hdo/cd/ src-test contains the code for Logger.


If you want to use it with unmodified FMX-100, you need a diode from 5.6V pin to LaunchPad USB Vcc and one NPN transistor for level shifting from HDO data pin to P1.3. And don't forget to connect ground. Simply connection would be as on picture for LoggerV1. It may be better to add RC filter on transistor input to suppress glitches. Or you can use optocoupler level shifting schema shown in 2nd post.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...