
So in my IoT project I mentioned that I might want to scale the application for multiple sensors. So let’s get going and doing just that!
The first thing was to understand how I can get a configuration item into my C example from the web server. This config being, how many sensors are we using. For that, I found these two examples really helpful:
https://curl.se/libcurl/c/getinmemory.html
This got me to this code:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/stat.h>
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>
#define I2C_DEV "/dev/i2c-1"
// Device address is set to 0x48, OR-ed with the address control lines.
// If these are not connected, the address will be 0x4b (0x48 | 0x03)
#define I2C_ADDR 0x4B
struct MemoryStruct {
char *memory;
size_t size;
};
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
if(ptr == NULL) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
int main (int argc, char **argv)
{
CURL *curl;
CURL *curl_handle;
CURLcode res;
int count = 0;
struct MemoryStruct chunk;
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
chunk.size = 0; /* no data at this point */
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
curl_handle = curl_easy_init();
/* specify URL to get */
curl_easy_setopt(curl_handle, CURLOPT_URL, "http://www.edward-jones.co.uk/IoT/config.php");
/* send all data to this function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
/* we pass our 'chunk' struct to the callback function */
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&chunk);
/* some servers don't like requests that are made without a user-agent
field, so we provide one */
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
/* get it! */
res = curl_easy_perform(curl_handle);
/* check for errors */
if(res != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
}
/* cleanup curl stuff */
curl_easy_cleanup(curl_handle);
/* we're done with libcurl, so clean it up */
curl_global_cleanup();
int numsensors = strtol(chunk.memory,NULL,10);
char str[1024];
char *message_fmt = "numsensors=%d\n";
sprintf(str,message_fmt,numsensors);
printf(str);
First thing is to fix the data array to be the right size:
unsigned char data[numsensors];
Now a quick update to how we post the data:
/* Now specify the POST data */
int i;
char str[1024];
for(i = 1; i <= numsensors; ++i)
{
char val[20];
char *message_fmt = "value%d=%d&";
sprintf(val,message_fmt,i,data[(i-1)]);
strcat(str, val);
}
int size = strlen(str); //Total size of string
str[size-1] = '\0';
strcat(str,"\n");
printf(str);
When I executed my program however nothing happened after Device Address Set output. I did a little debugging and learned something new like I always enjoy doing:
https://bytes.com/topic/c/answers/635788-program-hangs-after-printf-literal-string
So I add a new line character to my str variable before printf using the same strcat function and get:

Okay so now this should be sending the POST data to our log.php file quite nicely. Oh I forgot to mention the simple PHP script I setup for config.php:
<?php
$str = file_get_contents("/var/www/public_html/IoT/config.json");
$arr = json_decode($str);
//echo var_dump($arr);
echo $arr->numsensors;
?>
Now when I went to run all this setup, with my PHP file like so:
<?php
$str = file_get_contents("/var/www/public_html/IoT/config.json");
$arr = json_decode($str);
$num = (int)$arr->numsensors;
for ($x = 1; $x <= $num; $x++) {
$var = ('value' . strval($x));
$val = (((intval($_POST[$var]))/255)*100);
$arrayb[$var] = $val;
}
//$strJsonFileContents = file_get_contents("data.json");
//$array = json_decode($strJsonFileContents, true);
//array_push($array,$val);
$json = json_encode($arrayb);
file_put_contents("current.json", $json);
//$jsonb = json_encode($array);
//file_put_contents("data.json", $jsonb);
?>
I was getting some pretty weird results, everything was zero in my current.json (I removed the long term log for now and for simplicity so I’m only really interested in how my chilli’s currently are anyway). When I had print_r print the $_POST variable I saw really strange characters at the start of my variable name. I then googled a bit and quickly found this:
I had seen this before so knew it was something to do with strcat and that helped me narrow down the issue in only about 10 minutes but I’m sure without knowing that it’d probably kick you for a while. So I initialised all my strings with “= {0};” as the answer suggests and hey presto, good data again.
Great, now I’ve got my data on the server:

All working right? Wrong, I made the incorrect assumption that channel could be simply a numeric. Let’s go back to Kevin’s post that originated this whole thing in part 1.
https://kevinboone.me/adc.html?i=1
The line in question is specifically this:
int cmd = single_ended | dac_on_ref_off | channel;
What’s happening here is a bitwise OR operation where each of these are hexadecimal representations of a set of bits which when we OR them, will produce an 8 bit set of 1’s and 0’s to inform our ADC what we are doing. Let’s look at the table in the ADC’s datasheet:
https://www.ti.com/lit/ds/symlink/ads7830.pdf

Now I wanted to come up with some clever way of doing this, but honestly, the idea of it gave me a bit of a headache, the hex representation of binary, my usage of integer in the JSON request. So instead of doing something overly complex as this isn’t meant to be a super complex and efficient application I went with a simple look up table approach. I used google to plug in a binary and get the hex. For instance 100000, which would select Channel 4, get the hex and then plug this into the table and repeat until I got:
int chbytes[3] = {0x000,0x40,0x10};
I have only got 3 sensors so this will do for now. Next is to then rewrite that particular part of the code where we iterate and get the channel value:
/* Now specify the POST data */
int i;
char str[1024] = {0};
for(i = 1; i <= numsensors; ++i)
{
channel = chbytes[(i-1)];
cmd = single_ended | dac_on_ref_off | channel;
//printf("Command: %d \n",cmd);
// Write the command byte
write (i2c, &cmd, 1);
// Read the data byte
read (i2c, &data, 1);
//printf("%d", data[0]);
char val[20] = {0};
char *message_fmt = "value%d=%d&";
sprintf(val,message_fmt,i,data[0]);
strcat(str, val);
}
Great, now that all worked I could close that all down with the C code working as needed and POST in PHP doing all its tasks.
Only one part left, my android app. It was easy enough adapting my code to on startup get the config of the number of sensors in use. I did get caught out in that if you want a global variable in Kotlin, you declare it with var, not val. But the biggest thing was menu’s. I wanted a dynamically populated one whereas mostly you’ll define menu’s within the activity as a strictly coded XML. So how to get around this? When I looked at the android developer manual it was clear there was plenty of options but all in the documentation described a strictly defined version.
Oddly I find C to usually be the daunting thing to handle but I guess my years of experience have helped get those projects wrapped up easily despite the complexity. Then something as simple as JAVA/Kotlin and I’m down for the count, spending hours trying to figure out why one simple line doesn’t work. Hopefully, given time, I won’t have so many troubles.
I guess this is a perfect example of both sides of the Dunning-Kruger effect, I know that C is tricky and you can easily cause a segmentation fault. But at the same time, I’ve seen enough of them to know how to solve them and code it. After all, I’ve been doing Win32 programming since I was 13/14. But when it comes to Kotlin, I approach it with an assumption it would be easy and then get trapped and spend hours struggling.
Anyway, ultimately I needed a way to select which sensor we would be displaying so I needed some form of menu. I also needed to populate that menu based on the config.json we have from earlier so that both ends of the system know what the configuration is.
Well we’ve seen similar before in JSON requests for getting data so was a simple mostly copy and paste to get the config.json data:
val urlc = "http://www.edward-jones.co.uk/IoT/config.json"
// Initialize a new JsonObjectRequest instance
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET,
urlc,
null,
Response.Listener { response ->
// Do something with response
//mTextView.setText(response.toString());
// Process the JSON
try {
val texty = response.getString("numsensors")
this.numsensors = texty.toInt()
} catch (e: JSONException) {
e.printStackTrace()
}
},
Response.ErrorListener { // Do something when error occurred
}
)
The one new thing I had to learn was how to create global variables which after some quick googles appeared fairly simple and placing outside my OnCreate function but within my MainActivity I have:
var numsensors = 0
var selected = 1
Key features of this approach:
- Reduced overhead on JSON requests but caveat is that the configuration is only grabbed at initialization so if it changes it needs the app to be restarted.
- Default on startup is to have the first channel selected.
Now I created a simple button for the selection:

And now I needed to create code to pick up on a button press and then provide a way of selecting the sensor to display. I found that apparently the recommended approach is a dialog. Using this example:
https://developer.android.com/guide/topics/ui/dialogs.html#AlertDialog
Which allowed me to arrive at this code:
sensor.setOnClickListener(object : OnClickListener {
override fun onClick(view: View?) {
val items = IntArray(numsensors) { (it + 1) }
val stringArray = items.map { it.toString() }.toTypedArray()
val builder: AlertDialog.Builder = AlertDialog.Builder(me)
builder.setTitle("Pick a sensor")
builder.setItems(stringArray, DialogInterface.OnClickListener { dialog, item -> Toast.makeText(applicationContext, stringArray[item], Toast.LENGTH_SHORT).show()
selected = items[item]
})
val alert: AlertDialog = builder.create()
alert.show()
}
})
Now the tricky part was actually populating the list, and that’s where I ran across this really cool feature of kotlin as a language. You can actually initialize an array and simultaneously in one line populate the data based on a function! How cool is that?!
Many thanks to this post:
So that’s all the UI stuff done, worked a treat, now to actually have our RunRequest volley reflect this.
This actually seemed to be very easy and achieved with one line change in my function:
// Initialize a new JsonObjectRequest instance
val jsonObjectRequest = JsonObjectRequest(
Request.Method.GET,
urlb,
null,
Response.Listener { response ->
// Do something with response
//mTextView.setText(response.toString());
// Process the JSON
try {
val texty = response.getString(("value" + selected.toString()))
progressBar.progress = texty.toDouble().roundToInt()
text.text = texty.toDouble().roundToInt().toString() + "%"
} catch (e: JSONException) {
e.printStackTrace()
}
RunRequest(progressBar,text)
},
Response.ErrorListener { // Do something when error occurred
}
)
And finally, I had the capability to record and display both sensors! If I had selected, it would scale to 3 but I only have two chilli plants thus far so I only need 2 for now. Thanks for reading!