Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Jan Cumps
Published

Rolling Material Monitoring

Rolling material is expensive. Monitor it when it's out in the field. Get email stats for preventive maintenance. Find it again when lost.

IntermediateFull instructions providedOver 4 days6,272

Things used in this project

Hardware components

SIMATIC IOT2020
Siemens SIMATIC IOT2020
×1
XuLA2
Xess FPGA board with Xilinx Spartan 6
×1
Texas Instruments CC1310 LaunchPad
×1
Pulse Electronics Outdoor 868 & 915 MHz Antenna
×1
Velleman DIN-RAIL MODULE BOX B8006
×1
Velleman DIN-RAIL MODULE BOX - 2MG GD2MG
×1
Velleman DIN-RAIL MODULE BOX - 3MG GD3MG
×1
Velleman DIN-RAIL MODULE BOX - 4MG GD4MG
×1
Velleman DIN-RAIL MODULE BOX - 6MG GD6MG
×1
MEAN WELL DIN-RAIL MOUNTING CLIP DRP03
×1
VYNCKIER - FIX-O-RAIL 150
×1
Texas Instruments CC-Antenna-DK2 Antenna Kit
Sub-1 GHz and 2.4 GHz Antenna Kit for LaunchPad™ and SensorTag
×1
Pulse Electronics Outdoor 868 & 915 MHz Antenna
18 cm, Frequency [MHz] 868 - 928 Nominal Impedance [Ω] 50 Return Loss (dB) -8 Gain (dBi) 2 Polarization Omnidirectional
×1
Buchanan / TE Connectivity Fixed Terminal Blocks 2 POS. PC/MNT. TERM
PCB mount screw terminal connector blocks
×1
Arduino Proto Shield
Arduino Proto Shield
×1
IOT2020 3D Printed LCD Cover by Frederick Vandenbosch
×1
SimpleLink CC1x Sub-1 GHz Ultra-Low Power Wireless Microcontroller
Texas Instruments SimpleLink CC1x Sub-1 GHz Ultra-Low Power Wireless Microcontroller
×1

Software apps and online services

Sigfox
Sigfox
encrypted low energy, long range, good coverage. Industrial strength.
Eclipse NEON
TI-RTOS
Texas Instruments TI-RTOS
AWS DynamoDB
Amazon Web Services AWS DynamoDB
AWS IoT
Amazon Web Services AWS IoT

Story

Read more

Custom parts and enclosures

3D LCD Cover Concept Drawing

The drawing I mailed to Frederick as idea for the LCD Cover. For the excellent 3D design he made, check this blog: http://frederickvandenbosch.be/?p=2521

Schematics

IP-65 Outdoor 868 & 915 MHz Antenna specifications

Documentation of the external sub-1 GHz antenna

LCD and Terminals Shield

Shield to be mounted inside the IOT2020 with electronics for the LCD Display and terminals for the lines going external

Interconnectivity between modules

Connections between IOT2020, FPGA and CC1310

Power and (optional) WiFi

Power, UPS and the optional WiFI module for debug and development

Code

C++ Driver Class for LCD Displays - Header file

C/C++
Control LCD display with SIMATIC IOT2020 from a C++ program that runs on linux.
/*
 * Lcd.h
 *
 *  Created on: 20 mrt. 2017
 *      Author: jancu
 */

#ifndef LCD_H_
#define LCD_H_

#include "mraa.hpp"

namespace lcd_hitachi {


/**
 *
 * This code ported from:
 *
 * = Liquid Crystal Library for Arduino =

This library allows an Arduino board to control LiquidCrystal displays (LCDs) based on the Hitachi HD44780 (or a compatible) chipset, which is found on most text-based LCDs.

For more information about this library please visit us at
http://www.arduino.cc/en/Reference/LiquidCrystal

== License ==

Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (c) 2010 Arduino LLC. All right reserved.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */





class Lcd {
public:
	Lcd();

	~Lcd();

	void init( uint32_t u_rs,  uint32_t u_enable,  uint32_t u_d0,
			uint32_t u_d1,  uint32_t u_d2,
			uint32_t u_d3);

	void begin(uint32_t cols, uint32_t lines, uint32_t dotsize);

	void setCursor(uint32_t col, uint32_t row);

	void print(const std::string& input);

private:
	mraa::Gpio* _rs = NULL;
	mraa::Gpio* _enable = NULL;
	mraa::Gpio* _d0 = NULL;
	mraa::Gpio* _d1 = NULL;
	mraa::Gpio* _d2 = NULL;
	mraa::Gpio* _d3 = NULL;
	uint32_t _lcd_bitmode;
	uint32_t _numlines;
	uint32_t _currline;
	uint32_t _displayfunction;
	uint32_t _displaycontrol;
	uint32_t _displaymode;
	mraa::Gpio* _data_pins[8];

	void write4bits(uint32_t value);

	void write8bits(uint32_t value);

	void clear();

	void display();

	void lcdSetCursor(uint32_t col, uint32_t row);

	void lcdPrint(const char *str);

	/**
	 * set the direction of a given pin without altering other pins
	 * on the same port
	 *
	 */
	void command(uint32_t value);

	void send(uint32_t value, uint32_t mode);

	uint32_t lcdWrite(uint32_t value);

	void pulseEnable(void);

	/**
	 * delay for the given number of microseconds, for 80MHz processor
	 */
	void lcdDelayMicroSeconds(uint32_t us);

};

}

#endif /* LCD_H_ */

C++ Driver Class for LCD Displays - Source file

C/C++
Control LCD display with SIMATIC IOT2020 from a C++ program that runs on linux.
/*
 * Lcd.cpp
 *
 *  Created on: 20 mrt. 2017
 *      Author: jancu
 */

#include "Lcd.h"
using namespace mraa;

namespace lcd_hitachi {

// commands
#define LCD_CLEARDISPLAY 0x01
#define LCD_RETURNHOME 0x02
#define LCD_ENTRYMODESET 0x04
#define LCD_DISPLAYCONTROL 0x08
#define LCD_CURSORSHIFT 0x10
#define LCD_FUNCTIONSET 0x20
#define LCD_SETCGRAMADDR 0x40
#define LCD_SETDDRAMADDR 0x80

// flags for display entry mode
#define LCD_ENTRYRIGHT 0x00
#define LCD_ENTRYLEFT 0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// flags for display on/off control
#define LCD_DISPLAYON 0x04
#define LCD_DISPLAYOFF 0x00
#define LCD_CURSORON 0x02
#define LCD_CURSOROFF 0x00
#define LCD_BLINKON 0x01
#define LCD_BLINKOFF 0x00

// flags for display/cursor shift
#define LCD_DISPLAYMOVE 0x08
#define LCD_CURSORMOVE 0x00
#define LCD_MOVERIGHT 0x04
#define LCD_MOVELEFT 0x00

// flags for function set
#define LCD_8BITMODE 0x10
#define LCD_4BITMODE 0x00
#define LCD_2LINE 0x08
#define LCD_1LINE 0x00
#define LCD_5x10DOTS 0x04
#define LCD_5x8DOTS 0x00


Lcd::Lcd() {
	_lcd_bitmode = 0;
	_numlines=0;
	_currline=0;
	_displaycontrol = 0;
	_displaymode = 0;
	_displayfunction = 0;
}


Lcd::~Lcd()  {
	if (_rs) {
		delete _rs;
		_rs = NULL;
	}
	if (_enable) {
		delete _enable;
		_enable = NULL;
	}
	if (_d0) {
		delete _d0;
		_d0 = NULL;
	}
	if (_d1) {
		delete _d1;
		_d1 = NULL;
	}
	if (_d2) {
		delete _d2;
		_d2 = NULL;
	}
	if (_d3) {
		delete _d3;
		_d3 = NULL;
	}

}

void Lcd::init( uint32_t u_rs,  uint32_t u_enable,  uint32_t u_d0,
		uint32_t u_d1,  uint32_t u_d2,
		uint32_t u_d3) {
	_rs = 	new Gpio(u_rs, true, false),
			_rs->dir(mraa::DIR_OUT);
	_enable = new Gpio(u_enable, true, false);
	_enable->dir(mraa::DIR_OUT);
	_d0 = new Gpio(u_d0, true, false);
	_d0->dir(mraa::DIR_OUT);
	_d1 = new Gpio(u_d1, true, false);
	_d1->dir(mraa::DIR_OUT);
	_d2 = new Gpio(u_d2, true, false);
	_d2->dir(mraa::DIR_OUT);
	_d3 = new Gpio(u_d3, true, false);
	_d3->dir(mraa::DIR_OUT);
	_data_pins[0] = _d0;
	_data_pins[1] = _d1;
	_data_pins[2] = _d2;
	_data_pins[3] = _d3;

	// if we call this version of lcdInit, we're in 4 bit mode
	_lcd_bitmode = LCD_4BITMODE;

	// the mode is set to fixed character matrix, rows and bitmode(4/8)
	_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;




}
void Lcd::begin(uint32_t cols, uint32_t lines, uint32_t dotsize) {
	if (lines > 1) {
		_displayfunction |= LCD_2LINE;
	}
	_numlines = lines;
	_currline = 0;

	// for some 1 line displays you can select a 10 pixel high font
	if ((dotsize != 0) && (lines == 1)) {
		_displayfunction |= LCD_5x10DOTS;
	}

	// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
	// according to datasheet, we need at least 40ms after power rises above 2.7V
	// before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
	lcdDelayMicroSeconds(50000);

	// Now we pull both RS and R/W low to begin commands
	_rs->write(0);
	_enable->write(0);
	// no rw pin unless needed

	//put the LCD into 4 bit or 8 bit mode
	if (! (_displayfunction & LCD_8BITMODE)) {
		// this is according to the hitachi HD44780 datasheet
		// figure 24, pg 46

		// we start in 8bit mode, try to set 4 bit mode
		write4bits(0x03);
		lcdDelayMicroSeconds(4500); // wait min 4.1ms

		// second try
		write4bits(0x03);
		lcdDelayMicroSeconds(4500); // wait min 4.1ms

		// third go!
		write4bits(0x03);
		lcdDelayMicroSeconds(150);

		// finally, set to 4-bit interface
		write4bits(0x02);
	} else {
		// this is according to the hitachi HD44780 datasheet
		// page 45 figure 23

		// Send function set command sequence
		command(LCD_FUNCTIONSET | _displayfunction);
		lcdDelayMicroSeconds(4500);  // wait more than 4.1ms

		// second try
		command(LCD_FUNCTIONSET | _displayfunction);
		lcdDelayMicroSeconds(150);

		// third go
		command(LCD_FUNCTIONSET | _displayfunction);
	}

	// finally, set # lines, font size, etc.
	command(LCD_FUNCTIONSET | _displayfunction);

	// turn the display on with no cursor or blinking default
	_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
	display();

	// clear it off
	clear();

	// Initialize to default text direction (for romance languages)
	_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
	// set the entry mode
	command(LCD_ENTRYMODESET | _displaymode);



}
void Lcd::setCursor(uint32_t col, uint32_t row) {

	uint32_t row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
	if ( row >= _numlines ) {
		row = _numlines-1;    // we count rows starting w/0
	}

	command(LCD_SETDDRAMADDR | (col + row_offsets[row]));

}

void Lcd::print(const std::string& input) {

	for(std::string::size_type i = 0; i < input.size(); ++i) {
	    lcdWrite(input[i]);
	}
}

void Lcd::write4bits(uint32_t value) {
	uint32_t i;
	for (i = 0; i < 4; i++) {
		_data_pins[i]->dir(mraa::DIR_OUT);
		_data_pins[i]->write((value >> i) & 0x01);
	}

	pulseEnable();
}

void Lcd::write8bits(uint32_t value) {
	int i;
	for (i = 0; i < 8; i++) {
		_data_pins[i]->dir(mraa::DIR_OUT);
		_data_pins[i]->write((value >> i) & 0x01);
	}

	pulseEnable();
}

void Lcd::clear() {
	command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
	lcdDelayMicroSeconds(2000);  // this command takes a long time!
}

void Lcd::display() {
	_displaycontrol |= LCD_DISPLAYON;
	command(LCD_DISPLAYCONTROL | _displaycontrol);
}

void Lcd::lcdSetCursor(uint32_t col, uint32_t row) {
	int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
	if ( row >= _numlines ) {
		row = _numlines-1;    // we count rows starting w/0
	}

	command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
/*	// TODO implement lcdSetCursor()
	printf("\n*** start lcdSetCursor()");
	printf("\nThe cursor moves to row %u and column %u", row, col);
	printf("\n*** stop lcdSetCursor()");*/
}

void Lcd::lcdPrint(const char *str) {
	  while (*str)
		  lcdWrite(*str++);
/*	// TODO implement lcdPrint()
	printf("\n*** start lcdPrint()");
	printf("\nlcd print: %s", str);
	printf("\n*** stop lcdPrint()");*/
}

/**
 * set the direction of a given pin without altering other pins
 * on the same port
 *
 */
void Lcd::command(uint32_t value) {
	send(value, 0);
}

void Lcd::send(uint32_t value, uint32_t mode) {
	_rs->write(mode);

  // rw pin removed, add later if needed

  if (_displayfunction & LCD_8BITMODE) {
    write8bits(value);
  } else {
    write4bits(value>>4);
    write4bits(value);
  }
}

uint32_t Lcd::lcdWrite(uint32_t value) {
	send(value, 1);
	return 1; // assume sucess
}

void Lcd::pulseEnable(void) {
	_enable->write(0);
	lcdDelayMicroSeconds(1);
	_enable->write(1);
	lcdDelayMicroSeconds(1);    // enable pulse must be >450ns
	_enable->write(0);
	lcdDelayMicroSeconds(100);   // commands need > 37us to settle
}

/**
 * delay for the given number of microseconds, for 80MHz processor
 */
void Lcd::lcdDelayMicroSeconds(uint32_t us) {
	usleep(us);
}



} /* namespace lcd_hitachi */

C++ Driver Class for LCD Displays - Command Line Utility

C/C++
Control LCD display with SIMATIC IOT2020 from a C++ program that runs on linux.
When called without parameters: clear screen
When called with 1 parameter: print it on line 1
When called with 2 parameters: print first on line1, second on line 2

Existing text is always wiped out when calling this program
#include <iostream>
using namespace std;
#include "mraa.hpp"
#include "Lcd.h"

using namespace mraa;
using namespace lcd_hitachi;

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

	std::string *sText= NULL;
	std::string sEmpty("                ");

	cout << "Hello IOT2000." << endl;


	Lcd* lcd = new Lcd();

	lcd->init(8, 9, 4, 5, 6, 7);
	lcd->begin(16,2, 0);

	if (argc > 1) {
		lcd->setCursor(0,0); // move to the beginning of the first line
		sText = new std::string(argv[1]);
		lcd->print(*sText);
		delete sText;
		if (argc > 2) {
			lcd->setCursor(0,1); // move to the beginning of the second line
			sText = new std::string(argv[2]);
			lcd->print(*sText);
			delete sText;
		}
	}


	delete lcd;


	return MRAA_SUCCESS;
}

C++ Backlight Driver for the LCD

C/C++
Control LCD backlight with SIMATIC IOT2020. Accept brightness and on time as parameters, turn on backlight intensity and duration with those values. Then switch off.
#include <unistd.h>
#include <signal.h>

#include "mraa.hpp"

uint32_t uOnTime = 5;
float fValue = 100.0;
mraa::Pwm* pwm;



void
sig_handler(int signo)
{
    if (signo == SIGINT) {
    	pwm->write(0.0);
    	pwm->enable(false);
    	delete pwm;
    }
}

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

    signal(SIGINT, sig_handler);

    pwm = new mraa::Pwm(3);
    if (pwm == NULL) {
        return MRAA_ERROR_UNSPECIFIED;
    }

    pwm->enable(true);

    if (argc > 1) {
    	fValue = (0.0 + atoi(argv[1]))/100;
    }

    if (fValue > 0.0) {
    	if (argc > 2) {
    		uOnTime = atoi(argv[2]);
    	}
    	pwm->write(fValue);
    	sleep(uOnTime); // stay lit for given number of seconds
    }

    // switch off
    pwm->write(0.0);
    pwm->enable(false);
    delete pwm;

    return MRAA_SUCCESS;
}

vhdl port expander

VHDL
this code is from
http://www.csit-sun.pub.ro/courses/Masterat/Xilinx%20Synthesis%20Technology/toolbox.xilinx.com/docsan/xilinx4/data/docs/xst/hdlcode8.html

8-bit Shift-Left Register with Positive-Edge Clock, Serial In, and Parallel Out
-- http://www.csit-sun.pub.ro/courses/Masterat/Xilinx%20Synthesis%20Technology/toolbox.xilinx.com/docsan/xilinx4/data/docs/xst/hdlcode8.html
-- 8-bit Shift-Left Register with Positive-Edge Clock, Serial In, and Parallel Out

library ieee; 
use ieee.std_logic_1164.all; 
 
entity shift is  
  port(C, SI : in  std_logic; 
        PO : out std_logic_vector(7 downto 0)); 
end shift; 
architecture archi of shift is 
  signal tmp: std_logic_vector(7 downto 0); 
  begin 
    process (C) 
      begin  
        if (C'event and C='1') then  
          tmp <= tmp(6 downto 0)& SI; 
        end if; 
    end process; 
    PO <= tmp; 
end archi; 

vhdl port expander constraints

VHDL
VHDL constraints file with assignment to physical pins
# no clock needed. The master will provide it.

NET "C" CLOCK_DEDICATED_ROUTE = FALSE;

net SI loc=r2 | PULLDOWN;
net C  loc=t4 | PULLDOWN;
net PO<1> loc=f1 | IOSTANDARD = LVCMOS18;
net PO<2> loc=f2 | IOSTANDARD = LVCMOS18;
net PO<3> loc=e1 | IOSTANDARD = LVCMOS18;
net PO<6> loc=e2 | IOSTANDARD = LVCMOS18;
net PO<7> loc=c1 | IOSTANDARD = LVCMOS18;
net PO<4> loc=b1 | IOSTANDARD = LVCMOS18;
net PO<5> loc=b2 | IOSTANDARD = LVCMOS18;

IOT2020 to FPGA bridge code - port extpander

C/C++
Take an arbitrary number of integer parameters, and shift the 8 lowest bits to the FPGA, LSB first.
#include <iostream>
using namespace std;
#include "mraa.hpp"

using namespace mraa;


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

	int i = 0;
	int j = 0;
	uint8_t uData = 0U;


	cout << "IOT2000 FPGA Bridge" << endl;

	// hardware initialisation
	Gpio* d_pin = NULL;
	Gpio* c_pin = NULL;
	d_pin = new mraa::Gpio(11, true, false);
	c_pin = new mraa::Gpio(13, true, false);
	d_pin->dir(mraa::DIR_OUT);
	c_pin->dir(mraa::DIR_OUT);
	c_pin->write(0);
	d_pin->write(0);

	// if necessary, implement a reset and call it here.

	// for each argument passed,
	for(i = 1; i < argc; i++) {
		// convert to int
		uData = atoi(argv[i]);
		// and write each of the bits to the fpga, LSB first.
		for (j = 0; j < 8; j++) {
			c_pin->write(0);
			// write bit j. check http://stackoverflow.com/questions/2249731/how-do-i-get-bit-by-bit-data-from-an-integer-value-in-c
			d_pin->write((uData & ( 1 << j )) >> j);
			c_pin->write(1);
		}

	}

	return MRAA_SUCCESS;
}

C++ UART SigFox Linux command line utiity

C/C++
Sends the first 12 characye
#include <iostream>
#include "mraa.hpp"


using namespace mraa;

#define SIGFOX_PAYLOAD_LEN 12

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

	mraa::Uart *uart;

    uart = new mraa::Uart(0);

    uart->setBaudRate(9600);

    if (argc > 1) {
    	std::string s(argv[1]);
    	s.substr(0, ((2*SIGFOX_PAYLOAD_LEN)+3));
    	s.append(((2*SIGFOX_PAYLOAD_LEN)+3) - s.length(), '0');

    	// todo


    	// send exactly 12 bytes (represented in hex, so 24 characters after SFX. That's the max payload for Sigfox
    	uart->writeStr(s + "\r"); //

    }
    return 0;
}

CC1310_SIGFOX_industrial_monitor.zip

C/C++
CC1310 Sigfox and Beacon project for CCS 7.1
Please check the sigfox/nondistributable folder for closed source dependencies that you have to request from Texas Instrument and Sigfox.
No preview (download only).

CC1310 Display prototype

C/C++
CCS project for CC1310 that switches LCD display between Beacon and Sigfox logo.

As indicated, the vesion of SimpleLink that supports the display is incompatible with the closed source Sigfox lib that TI and Sigfox hand you.

To try this out, Go to Resource Explorer in CCS and check out the display_CC1310 project. Try to run and compile that and check if it works.
If it does, paste the content of zip file that I've attached here over the project.
No preview (download only).

sqlite scripts

SQL
scripts used to create and check database in sqlite3 command line
sqlite3
.open /home/jancumps/ProjectFiles/db/sigfox.sqlite
create table if not exists diag_sfx_msg (sfx_msg character(24));
select * from diag_sfx_msg;
select count(*) from diag_sfx_msg;


# command line tests
sqlite3 /home/jancumps/ProjectFiles/db/sigfox.sqlite "insert into  diag_sfx_msg (sfx_msg) values ('3300');"
sqlite3 /home/jancumps/ProjectFiles/db/sigfox.sqlite "select * from diag_sfx_msg;"
3300
sqlite3 /home/jancumps/ProjectFiles/db/sigfox.sqlite "select 'SFX'||sfx_msg from diag_sfx_msg;"
SFX3300

IOT2020 user button turns on LCD Backlight

C/C++
When user button pressed, the LCD backlight lights for 30 seconds
#include <iostream>
#include <fstream>
#include <unistd.h>
#include "mraa.hpp"




#include <sys/wait.h>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>

using namespace std;


int main()
	{

		//export the pin 63 (USER button)
		ofstream outfile;
		outfile.open("/sys/class/gpio/export");
		outfile << "63";
		outfile.close();


		int buffer;

		while(true)
		{
			//read out the status of the USER Button
			ifstream infile;
			infile.open("/sys/class/gpio/gpio63/value");
			infile >> buffer;
			infile.close();

			if(!buffer) {
				popen("/home/jancumps/ProjectFiles/iot2020_pwm 50 30", "r");


			}



			usleep(300);
		}

		return MRAA_SUCCESS;
	}

Credits

Jan Cumps

Jan Cumps

22 projects • 24 followers

Comments