Project tutorial
Rolling Material Monitoring

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.

  • 3,707 views
  • 5 comments
  • 19 respects

Components and supplies

Apps and online services

Sigfox logo rgb j7ilnvhq6o
Sigfox
encrypted low energy, long range, good coverage. Industrial strength.
Eclipse NEON
Ti rtos config tools
Texas Instruments TI-RTOS
Screen%20shot%202015 07 20%20at%206.11.48%20pm
Amazon Web Services AWS DynamoDB
Ha 2up iot
Amazon Web Services AWS IoT

About this project

Industrial rolling material is expensive. A bulldozer or a crane costs more than a house. And they're out there in the field, alone, unattended.

Wouldn't it be nice to receive an email every so often with the machine's current status, the time it has been running, and diagnostic info? And nicer to also receive an email immediately when it needs urgent attention? What if you could base your preventive maintenance schedule on field data? And wouldn't it be nice to find it so you can retrieve it when it has wandered off?

Highlights

  • Receive email diagnostics of in-the-field rolling material
  • Alerts when attention is needed
  • Reception in forests
  • Trace back stolen vehicle, rough position on internet map (accross continents), fine position via beacon
  • Industrial grade, Secure IoT design
  • Configurable hardware - digital I/O routed through FPGA
  • Reusable C++ libraries and code snippets
  • Roll-my-own Linux image with cron and sudo support.
  • integrated with Amazon Web Sevices (AWS)

In this design I focused on developing common patterns that can be used with the IOT2020. I used them here but documented them in other blog posts. Check the Related Blog section at the end for the list of additional blogs I created that are reusable for anyone working with the SIMATIC IOT2020 gateway.

Overview

This rolling material monitor sits inside your bulldozer. It collects data from the vehicle. Every so often it transfers that info to a commercial narrow band network. The results arrive in your email.

You can base your preventive maintenance on machine use and on diagnostics from the device. And leave your machine at the construction site longer.

Some images, like the one below, have angled brackets on both sides (< >). That indicates that there is more than one image. You can scroll using these angled brackets.

When a real mishap happens, the device can report home in real time. And if the power is down, it can fall back to low power mode and act as a beacon.

If it heads off to unknown far-away destinations, it'll silently tell you the region. Even when it's out of reach for your beacon finder. Even when the power is cut.

It uses safe communication (<-- see that, IoT?).

Hardware Overview

The project is designed around the IOT2020 and a radio transmitter. The IOT collects information about the vehicle in which it resides. Events and regular diagnostics info are stored in a database. The data that's relevant for maintenance planning gets collected, analysed and posted to the home base. In normal mode it sends a little data, a few times per day. Only when important events happen, the device dials home immediately.

The radio, a sub-1 GHz CC1310, sends that data. It sleeps most of the time. The IOT2020 wakes it up when a communication is needed. It will connect to the Sigfox network and send the few bits we've collected. When it hasn't heard from the IOT2020 during a given time, it will assume our bulldozer is in a stress situation (power down or power cut off) and will transform into a recovery device (there's power backup that only feeds the radio). It will now emit beacon signals to help with the precise location of the device. In this project I also provide the receiver that can be used to search and locate the device using the signal strength of that beacon.

I've also drawn additional components in the housing. These are the typical data gathering and control mechanisms: relais, DAC, ADC. They aren't essential to the core of the design. These are the typical extension ports that can be adapted to the particular rolling material that's going to be monitored. The FPGA is there for the same reason. It's symbolic for any hardware device that we can build to aid in the integration with the device.

IOT2020 Gateway

The SIMATIC IOT2020 is the core of the design. It has the duty to collect and persist maintenance data. It also initiates communication to home base. Because each of these functions take significant documentation real-estate, I've broken them out to separate blogs. They are hyperlinked in the text where appropriate and listed at the end of the article.

The i²c and UART signals are broken out. UART is used to talk to the radio module. The i²c signals communicate to DAC/ADC breakout module. The i²c line can be extended (within reasonable distance) to other modules, for instance a port extender with relais. In my design, a Xess XulA2 board symbolically plays that role.

Inside the IOT2020 box I've mounted an LCD text display (with a C++ driver lib) to show statistics and messages. The case got a new lid with an opening for the LCD, thanks to fellow contender Frederick.

An Arduino Proto PCB Shield hosts the extra hardware for the LCD shield - a PWM controlled switch transistor for the backlight and the contrast regulation potentiometer. The terminal blocks for the breakout signals (Buchanan screp terminals) are also soldered - and glued - on the proto shield.

Sub-1GHz CC1310 Transmitter and Sigfox Network

IoT should be secure. I don't want this to be the so-many-est project that doesn't deal with network safety. Communication is covered by the long-range low bandwidth Sigfox network.

The network operates in the unlicensed sub-1 GHz band. It has decent coverage (my country and all neighbours are covered). It's ideal for the kind of communication for this project: long range, very! low power, low data throughput, commercially supported.

For a design like this one, where a tiny amount of data needs to be transmitted every few hours - safe and with industrial rate reliability - this type of network is a good solution. And if you're working in the lumber business, you're guaranteed to have reception in the midst of a deep forest - not always the case with the mobile telephone service.

There are several layers of security. Communication is based on AES keys. The key that I'm using was generated by the radio chip manufacturer, based on the MAC address of the very IC in my project. A Sigfox operator has to white-list the physical device before it accepts communication. Messages are signed with the radio's hardware signature.

The sub-1 GHz radio is a Texas Instruments CC310. This chip combines a radio, an ARM controller and a sensor sub controller. It can (and in this project it does) run on very low power when it's not transmitting. Although I have plenty power from the bulldozer battery when everything is OK, I need decent lifespan when the radio is serving as last hope when things go wrong. And things go wrong on building sites at night. Theft of heavy duty rolling material is a thing.

I've documented the full setup of the CC1310 and Sigfox - including how to communicate with Texas Instruments and Sigfox to get keys and whitelist the device - in this blog: How-to: CC1310 Sub-1 GHz communication with SIGFOX network. If you're building a similar project, account for several weeks in your planning for this process to complete.

You can configure what the Sigfox network does with the messages you send. By default, they are made available on their portal site. Then there's a REST API that you can use to retrieve the data. Sigfox can also push the info to you. You can configure where it has to send the info to, based on events and data - for example your Node-RED service.

I've opted for email communication. It's a solution that works for almost any company - and there's no technology lock-in. I can choose an other option without a single change to my project hardware or firmware.

note: in the Extras section at the end of this article I show the integration of my device with Amazon Web Services.

Radio Operation during Power Outage

If the kit doesn't get power from the bulldozer, the sub-1 GHz radio stays alive. It falls back to a low activity mode.

It will try to contact the Sigfox network several times per day to tell that we're in distress. This will take care that I'll have a rough location of our vehicle (in case it's been stolen and the crooks cut off the battery). More frequently, it 'll send a beacon that can be detected by a sub-1 GHz receiver. The signal carries a fairly long range and the reception strength tells if you're moving towards the vehicle or away from it.

The fallback only requires one component: a power bank. There are types that can power a load while they are charging. When supply cuts, the take-over works automatically. The radio knows that the power is out because it no longer receives traffic from the IOT2020. A watchdog is reset each time we receive a keep-alive ping over the serial connection between the two. If the watchdog timer expires, the radio switches to the emergency operation mode.

External Antenna for the CC1310

The CC1310 LaunchPad has a PCB antenna with decent characteristics. For areas with good coverage this antenna does the job, even when the kit sits inside the case. For less optimal situation a decent rod antenna for the sub-1 GHz range helps to improve the range.

To switch over, I needed to remove a 0402 capacitor and solder a 0402 0Ω resistor. The PCB doesn't have the component names silkscreened. The image above helps.

The unit is built into a Velleman 4 unit DIN mount case (see Things section above), with the antenna in such a position that it 'll be protected by the project housing.

Fitting External Antenna on the CC1310 LaunchPad

Statistics:

FPGA

I have put an FPGA in this design for two reasons. The first one is that I want to become a VHDL expert and I use each opportunity to force myself to use it. But there's more.

An FPGA represents uncommitted logic. It can play the role of a complex device that handles many decisions in parallel (vs. the IOT2020 that has its parallel capacities limited to the number of processor cores). In a monitor design like mine, it can play the same role as the many input/output, timer and other modules typically found in a PLC. Because both the IOT2020 and the FPGA are programmable, this part offers digital expansion possibilities, and the possibility to reroute signals without physical intervention. An FPGA is an expensive component. But in our case, where flexibility and adaptability is a key factor, the price of one component is a ripple in the ocean. It'll be orders of magnitude cheaper than designing a custom discrete logic PCB for each vehicle.

Overview of the FPGA, the programs and a capture of the in- and outputs

You can use this component as a umphteen channel output expander or input expander - or as an uncommitted combination of the two - without additional hardware in our box. You could even (mini rant: I'm not a fan of that, long story ...) implement an additional ultra-fast but power hungry processor on it. In my vision of this project's architect however, it plays the role of any digital, adaptable, hardware logic required to monitor the machine we're protecting.

My design uses it as an arbitrary-bit output expander. It 'll turn two digital IOT2020 pins (data and clock) into as many pins as our project needs - limited only by the number of Spartan6 IO pins. The comparable logic IC would be a shift register. The FPGA can't compete with a fixed and committed 8 bit shift register. But once we're discussing more than 32 bits, flexible selection for in- and out-shifting, and the opportunity to take real-time decisions at those in/out combinations, the FPGA wins economically for both hardware and human costs (at modest production rate - once you go into mass production of the exact same digital design, this balance tips over to a custom board or an ASIC). If I were me in a commercial project rather than in a design contest, I would route each available pin of the IOT2020 to an FPGA pin, for maximum flexibility and future adaptability.

update: I have changed the VHDL design during the run of the project. It doesn't need that 9th clock pulse anymore.

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;

I've deliberately documented the VHDL in the Hardware section. In essence, it represents hardware.

The IOT2020 doesn't have to know what FPGA it's talking to. Only the logic levels, maximum speed and maximum voltages. I've chosen a Xess XulA2 because I'm a fanboy. No other reason, sorry. Oh yes, there's another reason: this is the board where the examples are better than those of the comparable dev-kits and those of the FPGA producers. I know this is a hard to validate measure - but it is a tangible real value that helps to get a professional working design in this not-so-well-documented world. When you are considering a project based on mine, the choice of FPGA isn't critical though. Nothing in my project locks that selection down to a particular chip or a hardware definition language. I use VHDL - but it could as well be done in Verilog without pulling out the soldering iron or the wallet.

The FPGA that I'm using has 32 pins broken out. We need 2 pins to communicate with the IOT2020 . That leaves 30 general purpose pins. I've configured 8 of them as outputs. The IOT2020 will clock 8 bits into the FPGA (using CH15 as CLK and CH16 as DATA). The output will reflect those 8 bits as values on pins CH23->30.

This table translates the assignments to the Xilinx Spartan6 FPGA pins.

The constraints file documents these assignments.

# 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<0> loc=h2 | IOSTANDARD = LVCMOS18; 
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;  

The FPGA does the buffering of the serial data until all bits are received and then sets them all at the same time. The FPGA doesn't have a fixed memory bit size, so I can just as well implement a 17 bit port extender. And as explained above, it can also read a number of pins and then serialise that data and push it to the IOT2020. So many options, so much freedom and flexibility for the designer. Here's a capture with a logic analyser of the four least significant bits when shooting the values 1 -> 9 to the FPGA

gdbserver localhost:2000 /home/ProjectFolder/iot2020_fpga_bitbang 1 2 3 4 5 6 7 8 9

And this is a capture of requesting the FPGA to set the pins with 5 (00001001b): pin 0 and 2 high, 1 and 4 low. The other 4 MSBs (I'm not probing them here) low. The capture is triggered on the input clock (coming from the IOT2020 going low.

/home/ProjectFolder/iot2020_fpga_bitbang 5

Loading he bit file to the XuLA2 flash is done with the Xess loader. Edit the constraint file properties in the Xilinx ISE and check that these properties are set:

  • Configuration Options -> ConfigRate : 10 MHz or higher (not important when programming the FPGA directly)
  • Startup Options -> StartUpClk : CCLK (set it to JTAG if you beam directly to FPGA)

Then run:

xsload --flash shift.bit

The Spartan6 FPGA will now be configured to act as port expander automatically when the system powers up.

Like every component in this project, the unit is mounted in a DIN Rail housing. This time it's a Velleman 3 unit wide box (see Things section above).

LCD Display

I'm using a Hitachi HD44780 compatible 16 x 2 LCD display. All data related pins are directly connected to digital pins of the IOT2020. A potentiometer on the Arduino Proto Shield regulates the contrast. The backlight intensity can be controlled by software (and is discussed in the Software section below). The backlight LED driver (an NPN transistor) is also mounted on that proto shield. The proto setup and schematics are shown in the pictures below. Use the scroll arrows to view them.

By default, the backlight will be off. When the user presses the IOT2020 User button - or automatically by the monitor program that runs on the IOT2020 - the light turns on for a given time, with a given intensity.

I had to make the following changes to the drawing above:

  • the RW input doesn't behave well when disconnected. I had to tie it to GND to make the display work.
  • Both VDD (pin 2) and backlight anode (pin 15) need to be connected to 5V instead of 3V3 to get enough contrast and enough backlight.

Software Overview

The project uses a limited set of solutions. I've tried to use tools that are well known by the industry. C and C++ for the embedded code. SQL for data access. I have deliberately not used Node-RED and MQTT - even though I like these solutions. I expect that a significant part of the submitted projects in this contest will use these tools and that they will be very well documented by the participants. Documented industrial projects with C and C++ talking to hardware on Linux are less common. My intention is to show that aspect of embedded design - and that includes writing libraries that can be reused by anyone.

IOT2020 Data Collector and Gateway

I'm using a limited set of techniques on the IOT2020. All functionality that talks to hardware is in C++ with the MRAA library:

  • talk to the CC1310 over UART
  • i²c to DAC and ADC
  • bitbang the LCD display and the FPGA
  • handle IOT2020 user button
  • PWM LCD backlight

Everything that's related to scheduling is done in Python. That language is also the glue between various blocks. Thank you Inderpreet - for solving my troubles with Python and SQLite!.

  • query the database at scheduled times and pass monitoring info to the communication module
  • send keep-alives to the communication module
  • gather diagnostic data
  • host the database

IOT2020 Display Driver for 16x2 LCD

See also:C++ LCD Display Driver for IOT2020

I've developed an Lcd class for the Hitachi family of LCD displays that uses the mraa libraries. The blog above explains its use and has the source code attached. Here's the public part of the class:

class Lcd { 
public: 
  Lcd() { 
    _lcd_bitmode = 0; 
    _numlines=0; 
    _currline=0; 
    _displaycontrol = 0; 
    _displaymode = 0; 
    _displayfunction = 0; 
  } 
  ~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 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 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 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 print(const std::string& input) { 
    for(std::string::size_type i = 0; i < input.size(); ++i) { 
        lcdWrite(input[i]); 
    } 
  } 
private: 
// ...

It's not difficult to use:

 #include <iostream> 
using namespace std; 
#include "mraa.hpp" 
#include "Lcd.h" 
using namespace mraa; 
using namespace lcd_hitachi; 
int main(void) { 
	cout << "Hello IOT2000." << endl; 
	Lcd* lcd = new Lcd(); 
	lcd->init(8, 9, 4, 5, 6, 7); 
	lcd->begin(16,2, 0); 
	lcd->setCursor(0,0); // move to the beginning of the first line 
	lcd->print("Hello, world!"); 
	delete lcd; 
	return MRAA_SUCCESS;
} 

I've made a Linux command line utility to drive the display. The code is available at the end of this article, in the Attachments section. You can call it with 0, 1 or 2 parameters:

  • 0: clear screen
  • 1: print parameter on line 1
  • 2: print parameter one on line 1, parameter 2 on line 2
# turn on backlight 100% 60 seconds (see later in this blog)
./iot2020_pwm 100 60 & 
# send two strings to the LCD display
./iot2020_lcd "sigfox" "sending data ..." 

IOT2020 PWM Control for 16x2 LCD

See also Hardware PWM on IOT2020 with C++.

The brightness of the backlight, as explained in the Hardware section, can be controlled by the IOT2020 with PWM.

Example PWM program that continuously increases the brightness:

   mraa::Pwm* pwm; 
   pwm = new mraa::Pwm(3); 
   if (pwm == NULL) { 
       return MRAA_ERROR_UNSPECIFIED; 
   } 
   fprintf(stdout, "Cycling PWM on IO3 (pwm3) \n"); 
   pwm->enable(true); 
   float value = 0.0f; 
   while (running == 0) { 
       value = value + 0.01f; 
       pwm->write(value); 
       usleep(50000); 
       if (value >= 1.0f) { 
           value = 0.0f; 
       } 
   } 
   delete pwm; 

The production program accepts a value between 0 and 100 as parameter and sets the brightness of the backlight accordingly. It turns the backlight off after a delay indicated by a second parameter.

IOT2020 Serial Communication Program

See also:Talk UART to any Microcontroller with C++ and IOT2020

This will talk to the CC1310 UART. It takes the data to be sent as the single parameter and will beam it over the serial wire. The size of the source in no way reflects the time it took me to make the IOT2020 talk to the CC1310 radio.

#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; 
} 

IOT2020 and the FPGA Port Expander

The FPGA functionality is documented in the Hardware section above. This section describes the IOT2020 part. Our program clock bits to the FPGA. When 8 bits are clocked in, 8 pins of the FPGA will switch accordingly.

On the IOT2020 we require 2 lines, regardless of the number of parallel pins we use on the FPGA. My plan is to use SPI for this - the protocol has a clock and data pin by default and would work straight away. But I have difficulties making SPI work with C++ and MRAA. I've raised a support ticket with Siemens.

The code below takes integers (as many as you want) as command line parameter, converts them into the discrete bits and sends the train off 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; 
} 

Communication with Sigfox

The next two sections explain how the interaction betwen the IOT2020 and the CC1310 works. To set the scene:

  • there's one way trafic, from IOT2020 to CC1310, over UART.
  • max 1 message, 12 bytes, each two hours
  • CC1310 has watchdog that kicks in when no life from IOT2020 for more than 2.5 minutes
  • IOT2020 sends a ping to entertain that watchdog everytwo minutes
  • if watchdog times out, CC1310 switched to "find-me-back" mode and send a beacon every 15 seconds

CC1310 FirmWare

There are 3 functional blocks:

  • the UART communication handler takes input from IOT2020,
  • the SIGFOX communication handler sends that data to the network,
  • the power outage handler with beacon functionality takes over when the IOT2020 appears to be down due to power cut.

I've documented core functionality of this application in two separate blog articles. One for the Sigfox functionality and one for the firmware of the long distance communication of the beacon. The mechanism to find the device back by measuring the beacon strength is documented here. So I'm reusing three of my designs here. They were always meant to become part of a single functional unit.

In this section I'll review what's specific to the Rolling Material Monitor. The whole design focuses on low power consumption. Whenever the code is not doing something useful, the unit goes into low power mode. I'm using RTOS with a combination of interrupts, semaphores and mailboxes to achieve that. The code never polls. It sleeps and acts on events.

The first specific design for this project is that I send information to Sigfox that comes from the IOT2020. I'm using UART here, interrupt driven.

This is the UART task:

Void fnTaskUART(UArg arg0, UArg arg1) 
{ 
   Error_Block eb; 
   Semaphore_Params sem_params; 
   UART_Params uartParams; 
   UART_Handle uart; 
   uint32_t i; 
   UART_init(); 
   /* 
    *  Initialize the UART parameters outside the loop. Let's keep 
    *  most of the defaults (e.g. baudrate = 115200) and only change the 
    *  following. 
    */ 
   UART_Params_init(&uartParams); 
   uartParams.writeDataMode = UART_DATA_BINARY; 
   uartParams.readDataMode = UART_DATA_BINARY; 
   uartParams.readReturnMode = UART_RETURN_FULL; 
   uartParams.readEcho = UART_ECHO_OFF; 
   uartParams.baudRate = 9600; 
   uartParams.readMode = UART_MODE_CALLBACK; // the uart uses a read interrupt 
   uartParams.readCallback = &UART00_IRQHandler; // function called when the uart interrupt fires 
   /* Create a UART for the console */ 
   uart = UART_open(Board_UART0, &uartParams); 
   if (uart == NULL) { 
       while (1); 
   } 
   Semaphore_Params_init(&sem_params); 
   sem_params.mode = Semaphore_Mode_BINARY; 
   SEM_uart_rx = Semaphore_create(0, &sem_params, &eb); 
   SEM_beacon = Semaphore_create(0, &sem_params, &eb); 
   i = 0u; 
   while (1) { 
       UART_read(uart, &uart_char, 1); // prime the uart bus to read the first character, non blocking 
       Semaphore_pend(SEM_uart_rx, BIOS_WAIT_FOREVER); // when a character is received via UART, the interrupt handler will release the binary semaphore 
       if ( 
               ((i==0) && (uart_char != 'S'))|| 
               ((i==1) && (uart_char != 'F'))|| 
               ((i==2) && (uart_char != 'X'))) { 
           i = 0; // discard, entry didn't start with SFX 
       } else if ( (i > 0) && (uart_char == 0x0A)) { 
           // submit string to the data layer 
           uart_input[i] = uart_char; 
           sendSigfox(uart_input); 
           i = 0u; 
       } else if (i == DATA_EXCHANGE_LEN) { 
           i = 0u; // todo raise buffer overflow error 
       } else { 
           uart_input[i] = uart_char; 
           i++; 
       } 
   } 
} 

The UART interrupt handler wakes up the UART task above, and also entertains the beacon watchdog.

/* EUSCI A0 UART ISR - The interrupt handler fr UART0 (USB) 
* this method releases the TI-RTOS binary semaphore SEM_uart_rx 
* and uartFxn() will process the received c 
*/ 
void UART00_IRQHandler(UART_Handle handle, void *buffer, size_t num) 
{ 
   Semaphore_post(SEM_uart_rx); 
   Semaphore_post(SEM_beacon); 
} 

Whenever the IOT2020 sends traffic to the CC1310, the latter is woken up and consumes the data. If that data turns out to be something that needs to be sent to Sigfox, the UART module creates a message and sends it to an RTOS mailbox (code below is a subset of the UART task above):

       Semaphore_pend(SEM_uart_rx, BIOS_WAIT_FOREVER); 
       if ( 
               ((i==0) && (uart_char != 'S'))|| 
               ((i==1) && (uart_char != 'F'))|| 
               ((i==2) && (uart_char != 'X'))) { 
           i = 0; // discard, entry didn't start with SFX 
       } else if ( (i > 0) && (uart_char == 0x0A)) { 
           // submit string to the data layer 
           uart_input[i] = uart_char; 
           sendSigfox(uart_input); 
           i = 0u; 
       } else if (i == DATA_EXCHANGE_LEN) { 
           i = 0u; // todo raise buffer overflow error 
       } else { 
           uart_input[i] = uart_char; 
           i++; 
       } 

This event wakes up the Sigfox communication task, the message is sent to the RTOS Sigfox mailbox.

void sendSigfox( char * msg) { 
   MsgSIGFOX      pMsg; 
  pMsg.msg = (sfx_u8 *)msg; 
   /* enqueue message */ 
   Mailbox_post(mbSIGFOX, &pMsg, BIOS_WAIT_FOREVER); 
   return; 
} 

The message that's sent around is a simple structure that points to the data received on UART:

typedef struct MsgSIGFOX { 
   sfx_u8 * msg; 
} MsgSIGFOX; 

The Sigfox task waits for the mail message to arrive (without timeout. It sleeps unless it's informed that a message arrived):

Void fnTaskSigfox(UArg arg0, UArg arg1) { 
   MsgSIGFOX d_msg; 
   sfx_error_t volatile err = SFX_ERR_NONE;// error returned from sigfox api functions. (For debug purpose) 
   /* Initialize the memory */ 
   SIGFOX_APP_memory_init(); 
   /* wait for mailbox to be posted by writer() */ 
   if (Mailbox_pend(mbSIGFOX, &d_msg, BIOS_WAIT_FOREVER)) { 
       SIGFOX_etsi_main(d_msg.msg); 
   } 
} 

The SIGFOX_etsi_main() task communicates with the network via the sub-1 GHz radio (ETSI is the standard here in Europe):

static void SIGFOX_etsi_main(sfx_u8 *msg) 
{ 
   sfx_error_t err; 
   sfx_rcz_t rcz= RCZ1; 
   /* To keep compatibilty with the TI modulation lib and AT cmd parser  */ 
   TxFrequency = rcz.open_tx_frequency ; 
   RxFrequency = rcz.open_rx_frequency ; 
   /* Update the global rcz structure to be used for  */ 
   g_rcz.open_tx_frequency = rcz.open_tx_frequency ; 
   g_rcz.open_rx_frequency = rcz.open_rx_frequency ; 
   g_rcz.macro_channel_width = rcz.macro_channel_width; 
   g_rcz.uplink_baudrate = rcz.uplink_baudrate ; 
   g_rcz.spectrum_access = rcz.spectrum_access; 
   /*! \brief Used for ARIB : {number of carrier sense repetition, carrier sense window, not_used}  according to sigfox_api.h : SIGFOX_API_set_std_config */ 
   sfx_u32 config_words[3]={3,5000,0}; 
   /* SIGFOX library init for ETSI mode */ 
   if ( err = SIGFOX_API_open(&rcz) != SFX_ERR_NONE) 
   { 
       /* todo: Manage the error  */ 
       error++; 
   } 
   /* Set the FCC macro channel configuration for FCC only as defined in sigfox_api.h */ 
   if ( err = SIGFOX_API_set_std_config ( config_words, 0)) 
   { 
       /* todo: Manage the error  */ 
       error++; 
   } 
   /* Execute the test sequence */ 
//    SIGFOX_DEMO_test_sequence(); // todo what is this??? 
   if (SIGFOX_API_send_bit(1, msg, 2, SFX_FALSE) == SFX_ERR_NONE){ 
       // todo: manage success 
   } 
   /* SIGFOX library close */ 
   SIGFOX_API_close(); 
} 
// used by the libraries 
void csTimer(UArg arg0){ 
   timerStatus = SFX_ERR_MANUF_WAIT_CS_TIMEOUT; 
} 

Then the CC1310 goes back to low power until a new message is posted to the RTOS Sigfox mailbox. The Sigfox service contract that I'm using (silver - it comes free with the CC1310 LaunchPad) allows 140 uplink communications per day, each maximum 12 bytes.

The second highlight is the beacon switch. The CC1310 has a software watchdog configured that is kicked every time a UART communication happens (see the UART interrupt above).

If the watchdog times out (no data received from the IOT2020 for a given time) it turns the CC1310 into a beacon device. Instead of talking to Sigfox, it sends a low speed long range signal every so often. That signal can be captured by our 'find-back receiver'.

The watchdog uses RTOS semaphore and timeout. A beacon task tries to get a hold of the semaphore, with a timeout configured (note that this doesn't require polling. It's event driven). Each time the UART receives data, it releases the semaphore. The beacon task sees that it got hold of that semaphore, so it knows the IOT2020 is still alive. If UART doesn't get any data from the IOT2020, it never releases the Semaphore. That causes a timeout in the beacon task.

Void fnTaskBeacon(UArg arg0, UArg arg1) { 
   // arg0 defines how long we wait before switchin to beacon mode. 10000000 = 145 sec 
   UArg uTimeout = arg0; 
   while (1) { 
       if(!Semaphore_pend(SEM_beacon, uTimeout) ) { // when a character is received via UART, the interrupt handler will release the binary semaphore 
           uTimeout = 10000; // let's send a signal often 
           sendBeacon(); 
       } else { 
           // at the first timeout, we've reduced the timeout to get a short time between beacons. 
           // when we do get data, we can (re)set the timeout as configured in the RTOS cfg file. 
           uTimeout = arg0; 
       } 
   } 
} 

That task will now reconfigure the radio for beacon service. In between beacon transmissions, the CC1310 will sleep as much as possible to maximise the time we can send out that signal (we're very likely running on the backup battery at that moment). To entertain the watchdog, the IOT2020 should send a dummy message to the CC1310 (1 char that isn't an 'S' is enough) faster than the watchdog time-out. Once new data arrives from the IOT2020, normal operation resumes.

See the Cron section below for more info. I've added cron to the Linux build and use it to schedule a wake-up message every two minutes.

Most of the time the controller is in the low power mode. It takes a physical character arriving on the UART to wake it up.

This CC1310 project is available for download as a ZIP file in the attachments. There are dependencies that In can't release because they are proprietary of Sigfox or dependent on the MAC address of your CC1310 chip. You have to request these dependencies from Sigfox and TI. I've written a blog on how to do that. The sigfox/nondistributable folder and its subfolders contain readme.txt files that explain what goes where.

This may be the most complex part of this exercise. Because it's not IOT2020 focused, I've avoided to go much deeper into this. Please check my blog specific to the subject.

IOT2020 Linux Image with CRON from Source

This project requires that some programs are scheduled. Cron isn't available on the example Linux image. With support of Siemens (see the Shortcuts and Mishaps section below) I managed to build the (at the moment of writing) unreleased version 2.1.3 of the image, with cron enabled in BusyBox.

I edited poky/meta-iot2000/meta-iot2000-example/recipes-example/busybox /busybox_1.24.1.bbappend and enabled the two following modules:

SRC_URI_append = " file://crond.cfg" 
SRC_URI_append = " file://crontab.cfg" 

in the poky/meta-iot2000/meta-iot2000-example/recipes-example/busybox/conf folder, I created a file for each of them.

(in the latest fetch, it seems the folder is

poky/meta-iot2000/meta-iot2000-example/recipes-example/busybox/files ?

crond.cfg

CONFIG_CROND=y

crontab.cfg

CONFIG_CRONTAB=y

The binaries are available on the IOT2020.

root@iot2020:~# whereis crond 
crond: /usr/sbin/crond 
root@iot2020:~# whereis crontab 
crontab: /usr/bin/crontab 

The cron daemon started as soon as I added a folder /etc/cron/crontabs.

root       359     1  0 18:50 ?        00:00:00 /usr/sbin/crond -c /etc/cron/crontabs 

I change the editor to nano before calling crontab:

export EDITOR=/usr/bin/nano 
crontab -c /etc/cron/crontabs -u root -e 

I scheduled the sigfox watchdog to run once every two minutes:

crontab -c /etc/cron/crontabs -u root -l 
*/2 * * * * /home/jancumps/ProjectFiles/crontab_sigfox_watchdog 

IOT2020 Linux Image with SUDO from Source

I've adapted the Example Image to include sudo. I added a line to the iot2000-build/conf/local.conf file:

IMAGE_INSTALL_append = " sudo"

Then I ran the image build commands and programmed my SD card with the new image.

I configured sudo to work for all users added to the group sudo:

> sudo visudo

removed comment before the line

%sudo   ALL=(ALL) ALL 
:qw

I then added a user for myself.

> useradd jancumps 
> passwd jancumps

Then assigned my user to the group sudo

> usermod -G sudo jancumps

Gave the user the right to be used as SSH logon account (I will revoke this from root).

> nano /etc/ssh/sshd_config
PermitRootLogin no
AllowUsers jancumps

That's it. I can now log on with my own account and elevate my rights with sudo when needed.

IOT2020 Linux Image with NTP from Source

I don't need Network Time Protocol in my design but there was a request from a fellow IOT2020 user on the Siemens forum. Here's how to add NTP:

Add this line to ./poky/meta-iot2000/meta-iot2000-example/recipes-example/busybox/busybox_1.24.1.bbappend:

SRC_URI_append = " file://ntpd.cfg" 

Then create a file ./poky/meta-iot2000/meta-iot2000-example/recipes-example/busybox/files/ntpd.cnf with content

(latest, ntpd.cfg ?)

CONFIG_NTPD=y 

Then run bitbake to generate the Linux image. NTPD is available. It can run as client or as deamon.

root@iot2000:~# whereis ntpd 
ntpd: /usr/sbin/ntpd 
root@iot2000:~# ntpd 
BusyBox v1.24.1 (2017-04-17 23:03:39 CEST) multi-call binary. 
Usage: ntpd [-dnqNw] [-S PROG] [-p PEER]... 

(you can see when I created the image creation in the BusyBox info :) )

IOT2020 Fixed IP and Name Resolution

On my network, DNS only works when I select DSCP in the iot2000setup application. Adding nameservers to /etc/resolv.conf get overwritten at reboot. I resolved this (warning, this may very well be against Linux ways of working. Feel free to correct me) by:

# remove symbolic link /etc/resolv.conf
> unlink /etc/resolv.conf 
#create new /resolv.conf file with following content:
  search example.com 
  nameserver 8.8.8.8 
  nameserver 192.168.1.1 
  nameserver 8.8.4.4 
> nano /etc/resolv.conf
# made /etc/resolv.conf immutable
> chattr +i /etc/resolv.conf 

Rebooted, and now the nameservers are persistent.

IOT2020 User Button

When the user presses the IOT2020 user button, the backlight of the LCD has to turn on. We have a backlight driver, so our button listener just has to call it.

#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; 
	} 

The button listener has to start up when the IOT2020 boots. Here's an excellent explanation.

create a shell script in /etc/init.d:

nano userbutton.sh 
#! /bin/bash 
/home/jancumps/ProjectFiles/iot2020_userbutton & 
exit 0 

Make it executable and register it in the startup handler:

chmod +x ./userbutton.sh 
update-rc.d userbutton.sh defaults 

On the next system startup, the user button lights up the LCD backlight with 50% brightness for 30 seconds.

The IOT2020 user button is hidden by the cover of my DIN case. But that cover is a bit flexible. I glued a soft plastic dome exactly above the button. When you press the cover on the right spot, it presses the IOT2020 user button. On the front side of the cover, I glued an icon on the spot where you have to push. This works very well.

Mechanics and Housing

The housings are from my brick-and-mortar shop, Elak, here in Brussels. I've used an industrial housing with two din rails, the Vinckier FIX-O-RAIL 150. It's big enough to host a number of DIN modules.

The IOT2020 occupies the best part of the upper rail. The other parts are all housed in a Velleman DIN-RAIL module (I use a 2,3,4 and 6 position one). The optional WiFi module - used to program the IOT2020 when needed and for debugging reasons, is hidden behind the scenes on a two-position DIN-RAIL clip.

The IOT2020 3D printed lid with opening for LCD display is drawn and made by fellow contender Frederick Vandenbosch.

This may be the most useful artifact resulting from the design contest. It can be used in many projects. Frederick uses it with an i²c front end. In my project I made a C++ class to talk to the Hitachi chip directly. Both designs work perfectly with this lib. Frederick's design is available on Thingiverse. If you use it in your project, give a shout-out and a link to his blog. It would be nice to see it used in many of the projects that participate in the contest.

Shortcuts, Mishaps, Accidents and Trade-offs, Lowlights

Because I want to resell the housing and reuse other parts, I haven't drilled holes to mount PCBs and other gizmos. I used double-sided tape and putty to put them in place. I have the necessary hardware and tools to mount them permanently but decided to not do that. I don't think it makes a difference to showcase the design but now you know that I use sneaky tricks.

I've skimped on the power conditioner for financial reasons. Most parts I purchased for this project (the DIN Rail case and mountable housings), I can either reuse or resell. Not so for the rather expensive components needed to deal with current inrush in vehicles with hydrolic pumps and valves. See section "If I was a Business" for more on this. If you think that I need to add it to the project, send your monies or components please.

Open: I wanted to use i²c to make the IOT2020 talk to the CC1310. That's the reason why I prototypd i²c with the DAC/ADC board. I spent (really: a lot) time to try and make the CC1310 work as an i²c slave - the controller's datasheet says it's supported. And the configuration objects in TI-RTOS have all the definitions to set it as a slave. In a deep debugging session I found a comment that the current RTOS session hasn't implemented it. I got a tweet from TI saying it'll be available in the next release. I've switched to UART now to make them talk, but In my design I have made the i²c connections between the two anyways. I can switch over when the time to upgrade has arrived.

Solved: When trying to switch out the CC1310 LaunchPad's PCB antenna with an external one, I damaged a tiny copper trace and I cut an 0402 capacitor in half with side clippers, while I was trying to trim a short jumper wire. I had to order new caps because I dind't have 100pF 0402 lying around.

Solved: I couldn't get SPI working with mraa and C. I had raised ticket https://support.industry.siemens.com/tf/ww/en/posts/mraa-and-spi-error-initialising-spi-bus/164148/?page=0&pageSize=10. It works in example image 2.1.3.

Open: To create a Character Device for the FPGA integration, I require the Linux kernel headers. I couldn't get Linux kernel header files generated for the IOT2020 SD card image 2.1.2. I raised unresolved ticket https://support.industry.siemens.com/tf/ww/en/posts/how-to-get-example-image-linux-kernel-headers/164494/?page=0&pageSize=10

Solved: I got cron to work on the example image, by building 2.1.3 from source with cron enabled in the bitbake configuration. I had raised ticket https://support.industry.siemens.com/tf/ww/en/posts/how-to-get-cron-on-the-example-image/164515/?page=0&pageSize=10.

Solved: The CC1310 talks to Sigfox and nicely switches to beacon mode after receiving no signal from the IOT2020. It locks after the first beacon however. This turned out to be a bug in my firmware. I have to close the radio handler before reconfiguring it betwen Sigfox and Beacon mode.

Solved: I had to downgrade the RTOS and SMartRF Studio application. I had the latest versions of both but they aren't compatible with the Sigfox closed source example for CC1310. These were the dependencies to solve:

  • SIgFox nondistributables depend on SmartRF Studio Version 2.4.1
  • SmartRF Studio 2.4.1 needs TI-RTOS distro, not the latest SimpleLink distro. TI-RTOS for CC13XX and CC26XX 2.21.0.06 works for me.

This became apparent when I integrated the Beacon mode with the working Sigfox application. Because for beacon mode I needed to generate dedicated radio settings with SmartRF Studio, I learned that API structures generated by the latest version have different members in the structures than 4.2.1 (even though both report that they are the same API version number in the source files). I uninstalled the latest version and rolled back to 4.2.1. Because it isn't downloadable from TI via a public page, I pretended to download the latest version and changed the version number in the download URL. It worked. Livehack.

Extras

In this project, I used emails to notify the home base. I've also set up an integration with Amazon Web Services (AWS). You can then integrate it into a web application, alerts via SMS, store it in an AWS hosted table, ...

Instructions are here. I was able to set this up without previous AWS experience by exactly following the steps without getting fancy.

Here's a trace of the IOT2020 sending the string abcdef (hex 616263646566) to the CC1310 and having Sigfox apply two rules: mail and AWS. The first image shows the AWS DynamoDB table record, the second image is the email that arrived.

If I Was a Business

If I'd make this project for business, I'd keep the architecture that I have now, but would miniaturise the different modules. The FPGA, CC1310, the LCD Displays and 4 ADC/DAC channels fit very well inside the box of the IOT2020. It even leaves place for a flat backup battery and a smaller but still decent antenna inside the box. The power conditioner would be an off-the-shelve DIN rail compatible module. The housing would change to have exit points for wiring and would be made splashproof - with dampened mounting points for the PCB.

Sigfox can integrate with the company's system instead of just email. The solution that Sigfox offers is rich. Safe- industry grade -integration with integration servers and ERP systems (say: SAP or Oracle E-Business Suite - I'm not namedropping here, I have integrating skills for both applications) is supported. A link with the maintenance module for intelligent planning and with the enterprise alert system in case of mayhem/theft is possible.

Interested?

Everything in this blog that's made by me is free for all. For those things that I re-used from others, please respect the original license. I didn't select a license for this project because I don't want to spend time to check what they restrict for others. For things made by me, do as you like - also if you want to use it for a commercial product. You don't owe me anything. Just don't restrict others along the road and don't complain or sue if it doesn't work in your situation though. Be an adult :).

Random Project Reaction Collection

How to Set Up the IOT2020

Debug IOT2020 C++ Program with Eclipse Neon and Windows10 64-bit

Drive an I2C DAC with C++ on IOT2020

How-to: CC1310 Sub-1 GHz communication with SIGFOX network

Talk UART to any Microcontroller with C++ and IOT2020

C++ LCD Display Driver for IOT2020

Hardware PWM on IOT2020 with C++

SQLite with C/C++ and Eclipse on Simatic IOT2020

Code

C++ Driver Class for LCD Displays - Header fileC/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 fileC/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 UtilityC/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 LCDC/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 expanderVHDL
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 constraintsVHDL
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 extpanderC/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 utiityC/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.zipC/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 prototypeC/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 scriptsSQL
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 BacklightC/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;
	}

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
Lcd lid3 9legztcfpd

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
Iot2020 hacksterio009 a aubmq6uzop
Interconnectivity between modules
Connections between IOT2020, FPGA and CC1310
Iot2020 hacksterio009 b 07hj4ozzmh
Power and (optional) WiFi
Power, UPS and the optional WiFI module for debug and development
Iot2020 hacksterio010 jopozt3m8v

Comments

Similar projects you might like

Plant Monitoring System using AWS IoT

Project tutorial by CJA3D

  • 30,463 views
  • 6 comments
  • 77 respects

WaterPi: Houseplant Remote Watering and Monitoring System

Project tutorial by Demirhan Aydin

  • 29,499 views
  • 14 comments
  • 128 respects

Aquarium Controlling and Monitoring Using Arduino + 1Sheeld

Project tutorial by Khaled Mohamed

  • 2,406 views
  • 1 comment
  • 12 respects

Heart Rate Monitoring System

Project tutorial by Team HRMS

  • 16,265 views
  • 2 comments
  • 29 respects

Smart Pool: Alexa Controlled Pool Manager

Project tutorial by Benjamin Winiarski

  • 1,355 views
  • 2 comments
  • 7 respects

Water Quality Monitoring Using MKR1000 and ARTIK Cloud

Project tutorial by Team Animo!

  • 7,075 views
  • 12 comments
  • 30 respects
Add projectSign up / Login