https://www.cascoda.com/
Content Index
- Introduction: What is KNX IoT
- Design considerations
- KNX IoT Development board
- ChiliCuisine: The Low Code software development environment
- Using the Hardware
- Example application: Battleship
Introduction: What is KNX IoT
KNX IoT is a new addition to the suite of KNX protocols, using a new physical transport medium. The transport medium is IPv6 (the later Internet protocol standard), thus future-proofing KNX as it can be used on IT equipment and all common IP physical transports.
Furthermore, KNX IoT guarantees interoperability with existing KNX wired or wireless infrastructure because it uses the same functional blocks and the same communication model as other KNX technologies. This is key as it allows KNX tooling, such as the Engineering Tool Software (ETS), to be used for the configuration of KNX installations, regardless of the transport used.
Specification
The KNX IoT specification in the KNX system specifications group (3_10_5 KNX IoT Point API) describes:
- A new transport layer based on IPv6, e.g., WiFi, Ethernet and Thread-based networks.
- A new communication/message protocol using CoAP and CBOR.
- The use of the same functional blocks as the other KNX transport layers.
- The use of the same S-mode message semantics as the other KNX transport layers.
- The use of the same configuration data to configure which device is talking to which device.
Interoperability
As mentioned in the Specification section above, KNX IoT conveys the same semantic data on the transport layer as all other KNX implementations. This is the key feature of KNX IoT that guarantees interoperability between KNX IoT and the other KNX technologies, while allowing the same tools to be used for configuration of KNX installations, regardless of the permutation and combinations of transport the transport types that used in an installation.
Security
Legislators in both Europe and North America have developed standards for IoT security. For example, the European Commission (EC) has adopted new Articles in the Radio Equipment Directive (RED) which establishes a regulatory framework for placing radio equipment that can:
- Communicate over the Internet, either directly or via any other equipment.
- Process personal data, traffic data or location data.
- Enable users to transfer money, monetary value or virtual currency.
These provisions become mandatory on 1 August 2024, at which point manufacturers of radio connected devices must be compliant. KNX IoT has been designed with this and the U.S. NIST cybersecurity standards in mind.
Thread compatibility
KNX IoT was developed in such a way that it can work with Thread. Thread is a low-power wireless mesh network-layer protocol, based on IPv6. Thread enables direct IP-based device-to-device and device-to-cloud communications. It reliably connects hundreds of products and includes mandatory security features. Thread networks do not have a single point of failure, thanks to their ability to automatically self-heal and reconfigure when devices are added or removed. Thread networks are also easy to set up and to work with.
Design considerations
Like any other KNX product, one needs to come up with the requirements of the device. This means that the data points and parameters that need to be implemented must be defined on a KNX level. This all also relates to which hardware peripherals will be used. To select hardware peripherals, a few things can be considered:
- Hardware availability
- Cost
- Quality/accuracy of the sensor/actuator
- Which hardware buses (interfaces) are used
- Power modes of the device
- Software driver availability
- etc
KNX IoT Development board
Figure 1 The KNX IoT Development Board.
Cascoda's KNX IoT Development Board provides everything you need to develop your KNX IoT over Thread application with the Cascoda Thread-certified Chili module. It features Cascoda's SMARTRange™ technology to provide long-range Thread connectivity for whole-house coverage.
The development board has headers for the MikroBUS, which enables developers to use a large suite of Click boards with KNX IoT. There are many kinds of Click boards, hence a large variety of different sensors and actuators can be built, ranging from environmental sensing (e.g., temperature, CO2) to PWM control (e.g., LED, fan).
The advantage of using the MikroBUS is that it standardizes a set of commonly used (bus) interface methods, like I2C, SPI and UART, and additional pins for PWM, interrupt, analog input etc. Supply options are 3.3V or 5V. Connecting the MikroBUS to the Chili module means that the connectivity at electronic signal level is straightforward. The development board has jumpers to enable/disable parts of the MikroBUS, to allow taking full advantage of the electronic inputs and outputs when possible. The Chili can also drive Click boards in low power mode, and a crowbar circuit is in place to cut the power to a Click board whenever needed.
Figure 3 Chili2S: the Thread module containing the MCU and Thread Radio
Integrated user interface
The development board also has integrated user interface functionalities, including 4 buttons and 4 LEDs, so that one can easily trigger a function and use the LED as feedback on a trigger. In addition to this, an E-INK display is supported.
Although E-INK displays do not have the fastest display speed out there, they have one big advantage over all other display technologies: they are very low power, only consuming power when the display is being updated.
Figure 4 Example screens of the Eink Display in action, extracted from one of our demo applications
The KNX IoT Development board can be powered with
- USB
- Battery (including rechargeable)
- External power
More details on the Development board can be found in the KNX IoT Development Board Data Sheet.
Click Boards: The hardware
The Micro Electronica Click boards can be found on the following website: https://www.mikroe.com/click
The click boards are categorized by functionality, i.e., what the Click board supports on the hardware.
There are more than 1500 different click boards available, covering all the main chipsets for sensing and actuation. Hence, lots of different hardware peripherals can be connected to the KNX IoT Development board.
If the current selection is not enough, it is possible to customize your own hardware with the proto click, which is a Click board specifically made to allow adding your own hardware on the mikroBUS.
Figure 5 Proto Click: prototype your own hardware
Click Boards: The software
In addition to needing a convenient means of connecting various hardware, the driver software to communicate with said hardware is also needed. This is solved by the MikroElektronika’s mikroSDK, which is the driver software supplied with the Click boards. The mikroSDK can be used in conjunction with the KNX IoT SDK running on the development board. Since the driver software is already provided(subject to the specific Click board),developers have a good starting point for integrating the hardware into the KNX IoT application, without the need to develop drivers from scratch. The drivers can be downloaded from LibStock - Community website for sharing libraries, projects and source codes (mikroe.com) or use GitHub.
Figure 6 GitHub repo of available click board drivers.
The advantage of using these drivers is that the low-level bus communication is already implemented. The application engineer must connect the application level of the driver to the KNX IoT code. For example, for a Thermo 3 click to read the temperature the example code in the SDK is as follows:
void application_task( void )
{
temperature = thermo_get_temperature( &thermo );
log_printf( &logger, "Temperature : %f\r\n", temperature );
}
Hence the function thermo_get_temperature( &thermo ) which returns the 32-bit float for a temperature value can easily be integrated into a KNX IoT temperature device.
ChiliCuisine: The Low Code software development environment
As discussed before, one already has in mind which KNX data points needs to be realized and which Click board hardware can be made available. So how can we put this together to prototype a KNX product quickly and easily?
The answer is to use ChiliCuisine™: a low code technology, which generates most of the code based on the specified KNX datapoints and parameters, requiring the user to only write the software to connect the application level of the driver to the KNX IoT infrastructure. ChiliCuising therefore brings the following benefits to the table::
- Avoid repetitive tasks
- Increase code quality
- Increase documentation quality
- Reduce complexity
- Only concentrate on what matters
- Quick turnaround time:
If input is correct: one can build something.
Software architecture
A KNX IoT stack contains a lot of layers to make the device function. This is depicted in Figure 7 below:
Figure 7 Software Stack running on the Development Board
This is a lot of software, but ChiliCuisine helps here: most of the code is already running on the KNX development board. An application developer only has to write code for the Application logic: e.g., making the connection between the hardware and the KNX IoT Software.
This is achieved by creating an input file which lists:
- Device Data, containing:
- Manufacturer info
- Device information
- …
- KNX Data points, identified by the URL
- KNX Properties, identified by the URL
Running ChiliCuisine will create the code for the “Data POINT API code” layer, which is the layer that implements the GET and PUT (optional) code of the data points and properties. The generated code consists of:
- Global variable that implements the data point/parameter
- Functions to update and retrieve the global variable
- Callback function that indicates that a PUT function has happened and that the global variable for that datapoint/parameter needs to be processed
- Function to trigger an s-mode message for a datapoint
Typically, there will be two scenarios that need to be implemented:
- Sending out an s-mode message
Update the other devices with a new value
Typically, the following sequence needs to be implemented:
- get the new value (from the hardware, e.g., a temperature value read by a sensor)
- set the new value on the datapoint
- send out the value to the other devices
- Receiving an s-mode message
Update your own state based on the value in the received s-mode message.
Typically, the following sequence needs to be implemented:
- get the new value from the stack
- use the value to trigger an action, e.g., update the EINK display, or toggle an LED.
Example of sending a switch value, implemented as a toggle.
void toggle_my_button( void *context )
{
// define temp variable
DPT_Switch sw;
// retrieve the current variable from the datapoint layer
app_get_DPT_Switch_variable(URL_PB_1, &sw);
// invert the value
sw = !sw;
// set the variable back to the datapoint layer
app_set_DPT_Switch_variable(URL_PB_1, &sw);
// sending s-mode message
oc_do_s_mode_with_scope(5, URL_PB_1, "w");
}
Example of handling a put callback, setting a LED on or off.
void put_callback(const char* url)
{
if (strcmp(url, URL_LED_1) == 0) {
/* update led */
DVBD_SetLED(LSAB_LED, !*app_get_DPT_Switch_variable(URL_LED_1, NULL));
}
}
The infrastructure of the application is also handled by the low code software development environment.
Functionality handled by the KNX IoT stack
The KNX implementation used is the KNX IoT Stack.
Figure 8 KNX IoT Stack homepage
The stack already implements the following functionality:
- Security
- Onboarding with SPAKE
- Sending/Receiving CoAP messages that are secured with OSCORE
- Device configuration: Creating, storing, and using the downloaded configuration data:
- Group object table
- Auth/at table
- Recipient and publisher table
- Handling the load state machine
- Creation of datapoints and properties
- Mechanism to send s-mode messages with the info from the Group Object table
- The stack supplies a function to trigger sending s-mode messages with the group address listed in the Group Object Table, e.g., a user of the stack does not have to know which group addresses are in use when the device is in run time mode
Note that all this functionality is built into the ChiliCuisine low code environment.
Functionality handled by the Cascoda BSP (SDK)
The BSP handles the following functionality:
- Implementing the porting layer of the KNX IoT Stack
- Thread communication, using Cascoda’s port of openthread.
- Persistent storage
- Software update
- Hardware acceleration for the security, hardware accelerated encoding/decoding of OSCORE.
- Board support functionality (Driver code for hardware on the Development board)
- Handling button presses
- Turning on LEDs
- Graphics library to use the EINK display.
Using ChiliCuisine
ChiliCuisine is implemented as a GitHub action. The input file(s) are in a GitHub Repo. The deliverables are being built by means of a PULL request and by creating a Release. The deliverables are then stored as an asset of the performed action. The deliverables are also downloadable as an asset of the release. Hence, anyone familiar with GitHub can use the low code platform. The input data contains a description of what is needed, and it can reference (e.g. include) C code. The C code can be added in a separated C file. Since the code is added in a separate file, any editor (with syntax highlighting) can be used to change the C code.
Deliverables by ChiliCuisine
The deliverables of the low code platform are:
- Binaries for the Development board:
- Sleepy end device
- End device
- Router enabled end device
- Binary to simulate the application on Windows, similar as KNX IoT Virtual.
- Input files for MT
- One can create a test project for ETS by running MT
- Note one has to have a MT version with license to create an ETS test project
- DoxyGen documentation of the generated “Data Point API code”
Figure 9 Example of the API documentation.
Hence everything is there to start with KNX IoT.
Getting Started with ChiliCuisine
Getting started with ChiliCuisine is easy: please contact sales@cascoda.com.
The support team will contact you and the following will be set up on request:
- Access to the ChiliCuisine GitHub action.
- Access to the multiple examples, so you do not have to start from scratch. Examples include:
- Lighting
- Dimming
- Color
- Scenes
- HVAC
- Temperature Sensor
- Humidity Sensor
- C02 Sensor
- EINK
- showing text
- showing graphics
- showing bitmaps
- Lighting
Note that all demos shown by Cascoda are created using ChiliCuisine.
Using the Hardware
System setup
To use the Development board, one also needs to have a Thread Border router. Cascoda’s Border router is called the KNX IoT Hub. This is the reason why Cascoda has a KNX-IoT development Kit. The development kit contains:
- 2 KNX IoT development boards
- KNX IoT Hub
Figure 10 KNX IoT Development Kit, containing two dev boards & a Thread border router
The System can be setup following the Getting Started Guide.
This guide shows all the steps to work with KNX IoT devices.
This includes:
- Creating a Thread network
- Adding the KNX IoT Device to the Thread network
- Installing the Cascoda Tool chain (to work with the Development boards)
- Updating/Flashing Firmware on the Development boards
- Using ETS6 with the device
Debugging
When something goes wrong one needs information to conclude what is wrong with the devices. There are various ways to get information:
- From the device, by means of printfs
- From the device, using the debugger (gdb)
- From the communications medium, using Wireshark.
Using Wireshark
Using Wireshark, combined with Cascoda’s Packet Sniffer, you can sniff and decode traffic occurring on the network. The Cascoda Packet Sniffer is a tool for capturing IEEE 802.15.4 traffic, used by Thread, KNX-IoT, Zigbee and more. It is ideal for analysing network problems, learning about IoT protocols and debugging IoT applications. Thanks to our industry-leading receive sensitivity, even the faintest packets can be captured, ensuring an accurate picture of the behaviour of the network.
The advantage of a network capture is that this is a non-invasive mechanism to allow you to observe what is going on on the network. The only requirements are that one has to have the security credentials of the Thread network and the OSCORE credential information. Wireshark can decode the layers, allowing you to see the CoAP and CBOR level communication between the devices on the network.
Figure 11 Wireshark showing Thread packets, decoding OSCORE and showing the CoAP and CBOR payloads
To get started with the Packet Sniffer, follow the getting started guide.
Using printfs
The printfs can be captured with “serial-adapter”. This tool is used to transport the contents of a printf statement to the PC. This can be done via USB (and even via UART if needed). When the KNX IoT Development board is plugged in to the PC via USB, simply start the “serial-adapter” program, and all printfs will be shown on screen.
Note that by default most printfs are disabled.
Note that enabling printfs will change the behaviour of the device, e.g., using printfs takes time on the processor, hence not all bugs can be found using printfs since timing will change.
Using the debugger
The following hardware debuggers are supported:
- Nuvoton NU-LINK PRO, see getting started guide for NU-LINK PRO.
- Segger J-Link, see getting started guide for Segger J-Link.
Example application: Battleship
Introduction
As an example, we will show the steps involved in building the Game “Battleship”, implemented with KNX IoT as the communication protocol between the 2 players.
Why build a game? Because we can! …and because it is a fun and interesting way to demonstrate how two devices can be set up to communicate using KNX IoT.. It also shows the functionality of using buttons and the EINK display for feedback. This example shows 2-way communication with complex data types.
KNX IoT is used as medium that conveys a shot at “A8” and the reply with “Hit, Cruiser”. A parameter is used to determine which player will start first.
Figure 12 Game flow for the 2nd player
Hardware
Battleship is a 2-player game; hence we need 2 development boards.
The game is visualized with a GUI; therefore, the development board need to be fitted with an EINK display. 3 buttons are used to control the game. The buttons can have actions on short or long presses depending on the screen that is shown.
Connecting the Eink Display to the Development board
The Development board has conveniently-placed pins on the side, to make for an easy connection with the E-Paper display. However, the header is not soldered on by default. If you're able to solder a header on, then connect the E-Paper display as shown in the image below. *NOTE: The pin mapping is highlighted by the use of colored rectangles surrounding the corresponding pins*.
Figure 13 Connect Eink display to the Development board via the header.
Otherwise, you can still connect the E-Paper display, because all the required pins are made available elsewhere on the board. However, the end-result won't look as neat. In any case, please refer to the image below for how the connections are made
Figure 14 Connect the Eink display to the development board via the MicroBUS.
Design
The battleship game follows the hasbro rules: 10x10 grid, 5 ships, single shot per turn.
Ships implemented, with size:
- Carrier (5 holes)
- Battleship (4 holes)
- Cruiser (3 holes)
- Submarine (3 holes)
- Destroyer (2 holes)
Each playing device will have:
- Ocean grid, displaying your own ships
- Target grid, displaying the enemy ships
What we need to model KNX-wise is defining datapoints for
- Sending & receiving shots using a coordinate.
- Sending and receiving shot status, with status
- Hit (H),
- Miss (M)
- Sunk (S)
- Ship type (T), for when a Shot is a Hit that Sunk the ship.
Figure 15 Sequence diagram of the interaction model
Building the Game
Battleship can be created with a single Functional block containing all datapoints.
There will also be a parameter to indicate who will start the game. This needs to be set by ETS.
The functional block will contain the following datapoints:
- SendShot
- Interface: If.o
- Array of int [x, y]
- url: /p/o_1_1
- ReceiveShot
- Interface: If.i
- Array of int [x, y]
- url: /p/o_1_2
- SendShotStatus
- Interface: If.o
- Array of [ bool:Hit/Miss, bool: Sunk, int: ShipType]
- url: /p/o_1_3
- ReceiveShotStatus
- Interface: If.i
- Array of [ bool:Hit/Miss, bool: Sunk, int: ShipType]
- url: /p/o_1_4
- SendReady
- Interface: If.o
- bool
- url: /p/o_1_5
- ReceiveReady
- Interface: If.i
- Bool
- url: /p/o_1_6
The parameter will be an integer (on the wire), indicating:
- url: /p/p_1_1
- 0 if the player is starting
- 1 if the player is second.
This information is then put in ChiliCuisine to create the datapoint application code. ChiliCuisine then creates functions to handle datapoints/parameters. The generic form of these functions is:
- Persistent_load_<type>
- Function to load the saved values at startup
- Persistent_store_<type>
- Function to store the values during the game
- app_is_<type>_url
- Function to check if an URL is implementing the type
- app_set_<type>_variable
- Function to set the value
- App_get_<type>_variable
- Function to retrieve the current value
Running ChiliCuisine creates the following infrastructure:
- URL to indicate the datapoint
- Functions to send and receive a shot
- Functions to send and receive a shot status
Example of the of the generated prototype for retrieving the Shot Status:
/**
* @ingroup DPT_Shot_Status
* @brief Get a DPT_Shot_Status array
*
* @param[in] url the url for the DPT_Shot_Status to get
* @param[out] out a pointer to the DPT_Shot_Status to copy out to
* If NULL, won't copy but return the global variable if available
* @return pointer to the (copied) DPT_Shot_Status <br>
* If out is NULL then returns the global variable for url if not
* flash only for this datapoint/param <br>
* If the variable is flash only then it will return out <br>
* If out is provided then it will return it UNLESS an error occurred while
* retrieving the variable from flash, in which case it will return NULL. <br>
* If an error occurs while getting the variable, returns NULL
*
* Example of how to use:
* ~~~{.c}
* DPT_Shot_Status my_var;
* if (app_get_DPT_Shot_Status_variable("/some/url", &my_var) == NULL) {
* //Something went wrong, maybe print an error message.
* return;
* }
* // do something with my_var
* ~~~
*/
const volatile DPT_Shot_Status* app_get_DPT_Shot_Status_variable(const char *url, DPT_Shot_Status* out);
The application code using the graphics library then can visualize the play.
The Graphics
The graphic library has functions to draw:
- lines
- circles
- text
This can be done per screen. Each screen can individually reach to certain button inputs, so that the controls can be implemented. The following game screens are available:
- GAME_INTRO
- Introduction
- CONTROLS_1
- How to use the controls
- PLACE_SHIPS
- Create your ocean grid
- READY_WAIT
- Wait until the game can start
- FIRE_SHOT
- Select x,y from target grid
- Show the target grid
- SHOT_INFO
- Update the target grid
- GAME_OVER
- One of the players will win, right?
The 3 buttons can be used for Navigation of the application, e.g., move between the different screens. For each screen, the behaviour of the buttons will be unique. For example, the buttons on the “Place Ships” screen will be used to position the ships. On the “Fire Shot” screen, the buttons will be used to select a position on the Target grid and to fire the shot.
The generated code via ChiliCuisine can be found on GitHub.
Some examples of the screen created with the code:
The introduction screen is used to explain the how the game starts. The numbers correspond to the buttons on the development board. The text for waiting on an opponent is displayed when no other opponent is signalling back that the game can start.
This is done via KNX IoT using SendReady and Receive Ready.
Hence this player is sending SendReady to the other player with the following code:
/**
* @brief Loads the READY_WAIT Screen to be drawn
*
* @returns true (display buffer filled, please draw me)
*/
static bool load_screen_ready_wait()
{
char buf[32];
snprintf(buf, 32, " opponent (%ds)", g_wait_time);
g_wait_time+=10;
display_setCursor(0,0);
display_puts(" Waiting for ");
display_setCursor(0,9);
display_puts(buf);
display_setCursor(0, 40);
display_puts("Hold (4) to exit");
DPT_Start ready = false;
app_set_DPT_Start_variable(URL_SENDREADY, &ready);
oc_do_s_mode_with_scope(5, URL_SENDREADY, "w");
TASKLET_ScheduleDelta(&screen_Tasklet, 10*1000, NULL);
return true;
}
The receiving is handled with the following code:
/**
* @brief Called when a Ready packet is received
* This is used to synchronize the two players and
* start them at the same time once boards have
* been setup.
*/
void onReceivedReady(const char *url)
{
if (strcmp(url, URL_RECEIVEREADY) != 0)
return;
if (g_screen_nr < READY_WAIT){
DPT_Start ready = false;
app_set_DPT_Start_variable(URL_SENDREADY, &ready);
return;
}
DPT_Start ready = *app_get_DPT_Start_variable(URL_RECEIVEREADY, NULL);
if (ready == false){
ready = true;
app_set_DPT_Start_variable(URL_SENDREADY, &ready);
oc_do_s_mode_with_scope(5, URL_SENDREADY, "w");
}else if (g_screen_nr == READY_WAIT) {
DPT_Param_Bool *start = app_get_DPT_Param_Bool_variable(URL_STARTING_PLAYER, NULL);
if (*start)
g_game.currentPlayerNo = PLAYER1;
else
g_game.currentPlayerNo = PLAYER2;
go_next_screen();
}
}
Figure 18 Game Control & placing the ships on the Ocean grid
The Game controls can be used in the ocean grid to place the ships. The placing of the ships is not communicated to the other player. The grids (both Target grid and Ocean) are a 10x10 array, which stores which coordinates are used, to track Hit/Miss Shots. For each grid, a set of ships are tracked, so that the hit/misses can be tracked for both sides. The game tracks whether all the ships of the opponent are sunk.
Figure 19 Showing the Target grid (2nd player) and the Ocean grid of the 1st player.
Function to receive a shot:
/**
* @brief Called when ShotStatus url is PUT
* This handles updating self player with
* information provided by the opponent about
* the shot just taken.
*/
void onReceivedShotStatus(const char *url)
{
if (strcmp(url, URL_RECEIVESHOTSTATUS) != 0)
return;
//can only recieve shotstatus when it is our turn
if (g_game.currentPlayerNo != g_game.myPlayerNo)
return;
// current player is ourselves
// put shot on opponent board
// our turn is complete!
enum PlayerNo player = swapPlayers(g_game.currentPlayerNo);
DPT_Shot_Status *status = app_get_DPT_Shot_Status_variable(URL_RECEIVESHOTSTATUS, NULL);
DPT_Uint_XY *shot = app_get_DPT_Uint_XY_variable(URL_SENDSHOT, NULL);
struct battleships_board *board = &g_game.boards[player];
struct battleships_cell *cell = &(board->cells[shot->_X][shot->_Y]);
struct battleships_ship *ship = &g_game.ships[player][status->_F3-1];
int ship_idx = get_occupy_idx_from_ship(ship);
if (!status->_F1 /*hit*/) {
cell->hitstate = HS_MISS;
}else{
cell->hitstate = HS_HIT;
cell->occupy_index = ship_idx;
if (status->_F2 /*sunk*/) {
ship->sunk = true;
ship->length = ship_lens[5-status->_F3];
//locate the ship
// it could be horizontal or vertical
// we could be at the edge of it
// if theres match at x-1, y then horiz
int px;
int py;
px = shot->_X;
py = shot->_Y;
// check up/down
if ( (py > 0 && board->cells[px][py-1].occupy_index == ship_idx)
|| (py < 9 && board->cells[px][py+1].occupy_index == ship_idx)) {
ship->orientation = VERTICAL;
while(py > 0 && board->cells[px][py-1].occupy_index == ship_idx)
py--;
ship->px = px;
ship->py = py;
} else
// check left/right
if ( (px > 0 && board->cells[px-1][py].occupy_index == ship_idx)
|| (px < 9 && board->cells[px+1][py].occupy_index == ship_idx)) {
ship->orientation = HORIZONTAL;
while(px > 0 && board->cells[px-1][py].occupy_index == ship_idx)
px--;
ship->px = px;
ship->py = py;
} else {
//something went wrong!!
PRINT_APP("BAD SHIP CALCULATION!\n");
}
}
}
//check if all our opponent's ships are sunk
int i;
struct battleships_ship *theirships = g_game.ships[swapPlayers(g_game.myPlayerNo)];
for (i = 0; i < 5; i++) {
if (!theirships[i].sunk)
break;
}
if (i != 5) {
//redraw
g_clean_redraw = false;
set_screen(SHOT_INFO);
g_game.currentPlayerNo = player;
} else {
set_screen(GAME_OVER);
}
}
Function to send a shot:
/**
* @brief Fire a shot at the opponent.
* Targeted square is the location of the shot.
* Will fire the shot even if there is a shot already
* there.
*/
static void game_fire_shot()
{
if(g_game.currentPlayerNo != g_game.myPlayerNo)
return;
DPT_Uint_XY shot_pos;
shot_pos._X = g_game.tx;
shot_pos._Y = g_game.ty;
app_set_DPT_Uint_XY_variable(URL_SENDSHOT, &shot_pos);
oc_do_s_mode_with_scope(5, URL_SENDSHOT, "w");
}
Figure 20 Feedback of a Hit and showing the Target grid.
Configuring Battleship with ETS
The ETS project to configure the Battleship game should include 2 devices, one for each player.
Since the game is written in such way that the devices are identical, one needs to configure 1 of the devices as “starting player”, this can be achieved by setting the start parameter on both devices.
The datapoints to be connected are:
- SendShot to ReceiveShot, in both directions
- SendReady to RecieveReady, in both directions
- SendShotStatus to ReceiveShotStatus, in both directions
Figure 21: ETS configuring 2 Batteships
-----------EOF-----
All images & graphics by Cascoda.