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?
- 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.
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 OverviewThe 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 GatewayThe 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 NetworkIoT 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.
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 CC1310The 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.
Statistics:
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.
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).
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.
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.
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
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 ..."
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 ProgramSee 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 ExpanderThe 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 SigfoxThe 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
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 SourceThis 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 SourceI'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 SourceI 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 ResolutionOn 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 ButtonWhen 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.
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, LowlightsBecause 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.
ExtrasIn 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'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 CollectionDebug 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
Comments