Modbus RTU Clock/remote dispaly. Wired RS-485 connection, remote powered 7-36V.
20190914_191558.jpg

Industrial format clock. Power up to 36V. Control via RS-485 Modbus RTU protocol.

Hardware

Combine LED board, Pro Mini, DC-DC, RS485 by wires.

20190914_191454.jpg
20190914_191538.jpg

Software

Coded in Arduino, want rewrite in Atmel Studio.

        #include "ModbusRtu.h"
#include "TM1637.h"
#include "timer-api.h"
#include "EEPROM.h"

//#include <avr/wdt.h>

//1024 0x03FF EEPROM, 32Kb FLASH, 2Kb RAM - atmega328
//512  0x01FF EEPROM, 16Kb FLASH, 1Kb RAM - atmega168
//512  0x01FF EEPROM,  8Kb FLASH, 1Kb RAM - atmega8

#define tm1637_CLK 5      
#define tm1637_DIO 6
#define buzzer     9
#define rs485ctr   4     
#define button1    2
#define statusled  13

#define msIDdef    45
#define resonans   2200

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long buzzerMillis = 0;

byte msID = msIDdef;

// meteodata
int temp = 32767; // 
byte humd = 255;  // 0 .. 99

// time config
byte hours  = 24;   // 0 .. 23
byte minute = 60;   // 0 .. 59
byte second = 60;   // 0 .. 59
byte year   = 100;  // 0 .. 99
byte month  = 13;   // 1 .. 12
byte day    = 32;   // 1 .. 28/29/30/31
byte bright = 7;    //0-7
//byte isRTC = 0;

int countdownMax = 60;
int todisplay = -1;
int countdown = 0;
int cd_buzzer = 0;
int screen = 0;
int tik = 0;
int ledState = LOW;
int light = 0;
int daycorection = 0;
int n1, n2, n3, n4;

uint16_t au16data[16];
uint16_t au16dataS[16];

int8_t state = 0;

const unsigned int interval = 500;  

TM1637 tm1637(tm1637_CLK,tm1637_DIO);
Modbus slave(msID,0,rs485ctr);

//http://www.uize.com/examples/seven-segment-display.html

void setup(){
  timer_init_ISR_1Hz(TIMER_DEFAULT);
  //wdt_enable(WDTO_8S);

  msID = EEPROM.read(0x0000);
  if (msID<1 or msID>247){
    msID = msIDdef;  
    EEPROM.write(0x0000,msID);
  };

  slave.setID(msID);
  slave.begin(9600);

  tm1637.init();
  tm1637.set(bright);

  daycorection = EEPROM_int_read(0x0001);

  pinMode(statusled, OUTPUT);
  //digitalWrite(statusled, HIGH);
  PORTB &= ~ B00100000;

  pinMode(button1, INPUT_PULLUP);
  // attachInterrupt(digitalPinToInterrupt(button1), button, FALLING  );

  pinMode(buzzer, OUTPUT); 

  screen=5;

  beepit(1000); 
  
  //humd=69;
  //temp = 800;

  //hours = 0;
  //minute = 0;
  //second = 0; 
}

void loop(){
  currentMillis = millis();

  if (cd_buzzer>0){
    if (currentMillis - buzzerMillis >= cd_buzzer){
      noTone(buzzer);
      cd_buzzer=0;
    };
  };
  
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    
    if (tik==0) screen=1;
    if (tik==9) screen=2;
    if (tik==14) screen=3;

    if (countdown>0){
      if (countdown%20==2){
        screen=1;
      }else{
        screen=4;
      };
    };

    if(todisplay>=0){
      screen=6;
    };
    
    tik++;
    if (tik>20) tik=0;
  };

  io_sh();
  state = slave.poll( au16data, 16 );
  if (state > 4) { 
    io_ch();
  };

    // годинник
    if (screen==1){
      if (hours==24 & minute==60) {
        tm1637.displayByte(0b01000000,0b11000000,0b01000000,0b01000000); // відображення --:-- 
      }else{
        n1 = (int)  minute % 10;
        n2 = (int) ((minute % 100)-n1)/10;
        n3 = (int)  hours % 10;
        n4 = (int) ((hours % 100)-n3)/10;
        tm1637.point(1);
        tm1637.display(0,n4);
        tm1637.display(1,n3);
        tm1637.display(2,n2);
        tm1637.display(3,n1);
        tm1637.point(0);
      };
      screen=0;
    };

    // відображення температури
    if (screen==2){
      if (temp<32767){
        int tempd = (int) abs(temp / 100);
        n1 = (int)  tempd % 10;
        n2 = (int) ((tempd % 100)-n1)/10;
        n3 = (int) ((tempd % 1000) - n2 - n1) / 100;
        if (temp<0){
          tm1637.displayByte(0,0x40);
        };
        if (n3>0){ tm1637.display(0,n3);}else{tm1637.displayByte(0,0x00);};
        if (n2>0){ tm1637.display(1,n2);}else{tm1637.displayByte(1,0x00);};
        if (n1>0 | temp==0){ tm1637.display(2,n1);}else{tm1637.displayByte(2,0x00);};
        tm1637.displayByte(3,0x63);
      };
      screen=0;
    };

    // відображення вологості
    if (screen==3){
      if (humd <100){
        n1 = (int)  humd % 10;
        n2 = (int) ((humd % 100)-n1)/10;
        tm1637.display(0,n2);
        tm1637.display(1,n1);
        tm1637.displayByte(2,0x63);
        tm1637.displayByte(3,0x5c);
      };
      screen=0;
    };

    // відображення зворотнього секундоміра
    if (screen==4){
      tm1637.displayByte(0b00000000,0b00000000,0b00000000,0b00000000); 
      tm1637.displayInt(countdown);
      screen=0;
    };

    // відображення адреси Modbus
    if (screen==5){
      tm1637.clearDisplay();
      tm1637.displayInt(msID);
      tm1637.displayByte(0,0x77);
      screen=0;
    };

    // відображення числа з регістру
    if (screen==6){
      if(todisplay>=0){
        //tm1637.clearDisplay();
        tm1637.displayIntZero(todisplay);
      };
      screen=0;
    };

    // тест екрану
    if (screen==9){
      tm1637.displayByte(0xff,0xff,0xff,0xff); //8.8.:8.8.
      screen=0;
    };

}

void io_sh() {
  au16data[0]=hours;
  au16data[1]=minute;
  au16data[2]=second;
  au16data[3]=year;
  au16data[4]=month;
  au16data[5]=day;
  au16data[6]=bright;        // яскравість LED екрану
  au16data[7]=temp;          // температура  
  au16data[8]=humd;          // вологість
  au16data[9]=msID;          // виставлення адреси слейва
  au16data[10]=countdown;    // запуск таймера з цих секунд 
  au16data[11]=countdownMax; // число секунд таймера
  au16data[12]=todisplay;    // відображеня числа на екрані
  au16data[13]=cd_buzzer;    // включення пищалки на n мілісекунд
  au16data[14]=light;        // рівень освітленості LDR
  au16data[15]=daycorection; // корекція годинника

  for (int i=0; i<16;i++){
    au16dataS[i]=au16data[i];  
  };  
};

void io_ch() {

  for (int i=0; i<6;i++){
    if (au16data[i] != au16dataS[i]){
      switch (i) {
        case 0:hours=au16data[0];break;
        case 1:minute=au16data[1];break;
        case 2:second=au16data[2];break;
        case 3:year=au16data[3];break;
        case 4:month=au16data[4];break;
        case 5:day=au16data[5];break;
      };
      screen=1;
    };
  }; 
  
  if (au16data[6] != au16dataS[6]){
    bright=au16data[6];
    tm1637.set(bright);
  };
  if (au16data[7] != au16dataS[7]){
    temp=au16data[7];
    screen=2;
  };
  if (au16data[8] != au16dataS[8]){
    humd=au16data[8];
    screen=3;
  };
  if (au16data[9] != au16dataS[9]){
    msID = (byte) au16data[9];
    slave.setID(msID);
    EEPROM.write(0x0000,msID);
    screen=5;
  };
  if (au16data[10] != au16dataS[10]){
    countdown=au16dataS[10];
    screen=4;
  };
  if (au16data[11] != au16dataS[11]){
    countdownMax = au16data[11];
  };
  if (au16data[12] != au16dataS[12]){
    todisplay = au16data[12];
    if (todisplay<0){
      screen=1;  
    }else{
      screen=6;
    };
  };
  if (au16data[13] != au16dataS[13]){
    beepit(au16data[13]);
  };  

  if (au16data[15] != au16dataS[15]){
    daycorection = au16data[15];
    EEPROM_int_write(0x0001,daycorection);
  };
};

void timer_handle_interrupts(int timer) {
  //wdt_reset();
  if (hours!=24 & minute!=60) {
    second++;
    if (second>59) {
      second=0;
      minute++;
      minute_upd();
      screen=1;
      if (minute>59) {
      minute=0;
      hours++;
      hours_upd();
      if (hours>23) {
        hours=0;
        };
      };
    };
  };
  if (countdown>0){
    countdown--;
    screen=4;
    if (countdown==0){
      beepit(1000);  
    };
  };

  if (ledState == LOW) {
    ledState = HIGH;
    PORTB &= ~ B00100000;
  } else {
    ledState = LOW;
    PORTB |= B00100000;
  };
  //digitalWrite(LED_BUILTIN, ledState); 
  
};

void button() {
  countdown=countdownMax;
  screen=4;
}; 

void beepit(int timeus) {
  tone(buzzer, resonans);
  cd_buzzer = timeus;  
  buzzerMillis = currentMillis;
}; 


void minute_upd(){
  
}

void hours_upd(){

  if (daycorection<5000){
    int hourcor = (int)  daycorection % 24;
    if (hours == 0){
      second = second + (daycorection-hourcor*24);
    };
    if (hourcor>0){
      second = second + hourcor;
      if (second>=60){
        int minutc = (int)  second % 60; 
        second = second - minutc*60;
        minute = minute + minutc;
      };
      if (second<0){
      
      };
    };
    
  };
};

int EEPROM_int_read(int addr) {    
  byte raw[2];
  for(byte i = 0; i < 2; i++) raw[i] = EEPROM.read(addr+i);
  int &num = (int&)raw;
  return num;
}

void EEPROM_int_write(int addr, int num) {
  byte raw[2];
  (int&)raw = num;
  for(byte i = 0; i < 2; i++) EEPROM.write(addr+i, raw[i]);
}
    
Yuriy Rudyy

Categories