PICO hangs on micropython object creation - c

I recently started working on a small driver for the raspberry pico and the bme280 sensor. I wanted to use the official bosh API written in C and therefore decided to write all the code in C using the micropython C api to write usermodules. I managed to get my code compiled into a UF2 file and my module shows up when I try to list the modules with help('modules'). When I import my module the class with the driver code shows up in dir(mymodule) but when I try to create an object the terminal connected to the PICO hangs and doesn't respond anymore.
typedef struct {
mp_obj_base_t base;
uint8_t sda;
uint8_t scl;
uint8_t i2c_address;
} BME280_obj_t;
const mp_obj_type_t BME280_class_type;
STATIC mp_obj_t BME280_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
mp_arg_check_num(n_args, n_kw, 2, 2, true);
BME280_obj_t* self = m_new_obj(BME280_obj_t);
self->base.type = &BME280_class_type;
self->sda = mp_obj_get_int(args[0]);
self->scl = mp_obj_get_int(args[1]);
self->i2c_address = n_args <= 2? BME280_I2C_ADDR_SEC : mp_obj_get_int(args[2]);
return MP_OBJ_FROM_PTR(self);
}
STATIC const mp_rom_map_elem_t BME280_locals_dict_table[] = {
// for testing purpose i removed all methods from the class
};
STATIC MP_DEFINE_CONST_DICT(BME280_locals_dict, BME280_locals_dict_table);
const mp_obj_type_t BME280_type = {
{ &mp_type_type },
.name = MP_QSTR_BME280,
.print = BME280_print,
.make_new = BME280_make_new,
.locals_dict = (mp_obj_dict_t*) &BME280_locals_dict,
};
STATIC const mp_rom_map_elem_t bme280_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_bme280) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_BME280), (mp_obj_t)&BME280_class_type }
};
STATIC MP_DEFINE_CONST_DICT(bme280_module_globals, bme280_module_globals_table);
// module registration
const mp_obj_module_t bme280_user_cmodule = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&bme280_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_melopero_bme280, melopero_bme280_user_cmodule, 1);
I think the problem relies somewhere in the initialization procedure since it does not go further... Maybe there is something that micropython is doing behind the scenes that I'm ignoring. There is not so many documentation on writing usermodules in C... any help/hints/ideas are greatly apreciated :)
EDIT+ANSWER:
Yes I got the example to build and so I started to strip down my code to the minimum necessary to get it almost identical to the example and so I found the error...
The problem was that I used a different name for the class type in the declaration: BME280_class_type and in the definition: BME280_type

You have .print defined but it doesn't exist in your code.
const mp_obj_type_t BME280_type = {
{ &mp_type_type },
.name = MP_QSTR_BME280,
.print = BME280_print,
.make_new = BME280_make_new,
.locals_dict = (mp_obj_dict_t*) &BME280_locals_dict,
};
There is an example of writing a proper class print function here
However, this should be enough to at least satisfy the print requirement. Paste this into your code and see if it stops hanging.
STATIC void BME280_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
(void)kind;
BME280_obj_t *self = MP_OBJ_TO_PTR(self_in);
mp_print_str(print, "BME280");
}
Also, I'm sure you just left this out of your example, otherwise you wouldn't have been able to build it, at all, but for the sake of being thorough ~ you have to include the proper header files.
#include <stdio.h>
#include "py/runtime.h"
#include "py/obj.h"
Edit: I was curious. You said you got everything to build, but it hangs. I commented out the print function for one of my C MODULE classes, and it would not build. This tells me that you (like the header) decided to just leave this part out of your example. This wastes time. How can you be helped if your example is creating errors that do not exist for you? However, even though this answer is now wrong, I am going to leave it up as an example of why answer-seekers shouldn't pick and choose what to post. Just post ALL of the relevant script to your problem and let us figure out the rest. I'd probably have an answer for you if I wasn't solving the wrong problem. Actually, I do see your problem, you're creating your module with mixed namespaces.

Related

App crashing when linking Discord C SDK and not displaying any logs

I am developing a macOS app that uses the Discord SDK (which is written in C). After I import it into another C file that encapsulates all of the logic to initialise it, and then link this C file to my Swift project, the app crashes upon launch, and no errors or crash information is displayed on the output. I have tried importing this same C file into a command line application written in pure C, and it works perfectly.
I would like to know if there's any way to check the error logs outputted from the C code, and where to find it.
Here is the code for the C function I am calling, the crash occurs on DiscordCreate():
void initializeDiscord() {
struct Application app;
memset(&app, 0, sizeof(app));
struct IDiscordActivityEvents activities_events;
memset(&activities_events, 0, sizeof(activities_events));
struct DiscordCreateParams params;
DiscordCreateParamsSetDefault(&params);
params.client_id = CLIENT_ID;
params.flags = DiscordCreateFlags_Default;
params.event_data = &app;
params.activity_events = &activities_events;
int ver = DISCORD_VERSION;
DiscordCreate(ver, &params, &app.core);
app.activities = app.core->get_activity_manager(app.core);
app.application = app.core->get_application_manager(app.core);
app.activity_manager = app.core->get_activity_manager(app.core);
struct DiscordActivity activity;
memset(&activity, 0, sizeof(activity));
strcpy(activity.details, "Test");
strcpy(activity.state, "Test");
strcpy(activity.assets.large_text, "test");
strcpy(activity.assets.large_image, "test");
activity.timestamps.end = (unsigned)time(NULL) + 120;
app.activity_manager->update_activity(app.activity_manager, &activity, callbackData, callback);
for (;;) {
app.core->run_callbacks(app.core);
usleep(16 * 1000);
}
}
Turns out that disabling app sandbox solved the problem. This is probably an issue with the Discord SDK trying to do something that the sandbox doesn't allow.
Still bummed that Xcode showed no warnings, errors or any output at all to indicate that was the issue, but at least it's working now.

After setting change_state in class_init function, nothing else works

I followed the gstreamer's tutorial on how to write plugins, and my goal is to create a video sink. For now I will just post relevant code, but if needed, I can upload whole plugin code somewhere. It will be an opensource anyway.
The class structure looks like this:
struct _GstIviOpenglSinkClass
{
GstVideoSinkClass base_iviopenglsink_class;
};
and my class_init() function looks like this:
static void
gst_iviopenglsink_class_init (GstIviOpenglSinkClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
GstVideoSinkClass *gstvideosink_class = GST_VIDEO_SINK_CLASS (klass);
GST_DEBUG ("START");
gobject_class->set_property = gst_iviopenglsink_set_property;
gobject_class->get_property = gst_iviopenglsink_get_property;
gobject_class->finalize = gst_iviopenglsink_finalize;
gst_element_class_add_pad_template (gstelement_class,
gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
gst_caps_from_string (VIDEO_SINK_CAPS)));
gst_element_class_set_static_metadata (gstelement_class,
"desc", "Sink/Video",
"desc",
"me <me#com.com>");
gstbasesink_class->get_caps = gst_iviopenglsink_get_caps;
gstbasesink_class->set_caps = gst_iviopenglsink_set_caps;
/* makes problems?!? */
gstelement_class->change_state = gst_iviopenglsink_change_state;
gstvideosink_class->show_frame = gst_iviopenglsink_show_frame;
GST_DEBUG ("END");
}
If I leave the class_init function like this, then the callbacks set_caps, get_caps, and show_frame, are not working (not being called at all). If I outcomment the assignment of the change_state, then these 3 callbacks are being called.
Am I doing something wrong in the class_init function, or is the problem lieing somewhere else? What can I do to debug this?
This comment was correct - the problem is indeed in the function dealing with change_state. I took a look into the source code for waylandsink and found this weird line:
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
I have no idea what that line is doing, but apparently it is needed. And it is nowhere explained neither in the tutorial for plugins, nor in the page describing GstElementClass.
If someone can explain why is that needed, I would really appreciate it :)

CppUTest not running on target - How to "fake" register

I'm doing Unit Testing of embedded C code without running on target hardware.Here is one part of the code:
uint8 tempReadback = 0;
write_to_i2c( msg->addres, msg->value );
tempReadback = read_from_i2c( msg->addres);
if( tempReadback == msg->value )
{
somethingA;
}
else
{
somethingB;
}
The function write_to_i2c() writes a value to a specific register. The function read_from_i2c() reads back the value from the register. Further, I'm using the variable tempReadback to compare if the read back value is the same as the written one. So far OK, and this works on the target hardware. Now I'm doing the Uni Tests without running the code on target Hardware (Software in the Loop). This means, the expression tempReadback == msg->value will never be true (tempReadback is 0) and I will run each time in the statement somethingB. Is there any way to fake the register read back? I'm using CppUTest as framework.
Will be thankful!
CppUTest is the perfect fit for embedded C development, since it is the only testing framework that allows to mock free functions (in your case, write_to_i2c() and read_from_i2c()).
Now, you should really be reading the CppUTest documentation or the excellent book Test Driven Development for Embedded C.
Anyway, the following code shows how to do it.
Your Unit Under Test (UUT), written in a way that is compilable (next time you ask a question on SO please make the effort):
#include "temperature.h"
#include "i2c.h"
void somethingA(void) { }
void somethingB(void) { }
void temperature_do(uint8_t address, uint8_t value) {
write_to_i2c(address, value);
const uint8_t tempReadback = read_from_i2c(address);
if (tempReadback == value) {
somethingA();
} else {
somethingB();
}
}
As you wrote, we need to "fake", or more exactly we need to "mock" write_to_i2c() and read_from_i2c(). We put the mocks in a separate file, say i2c_mock.cpp, so that when building the Unit Test, we link against the mock and not against the real implementation:
extern "C" {
#include "i2c.h"
};
#include "CppUTestExt/MockSupport.h"
void write_to_i2c(uint8_t address, uint8_t value) {
mock().actualCall(__FUNCTION__)
.withParameter("address", address)
.withParameter("value", value);
}
uint8_t read_from_i2c(uint8_t address) {
mock().actualCall(__FUNCTION__)
.withParameter("address", address);
uint8_t ret = mock().returnIntValueOrDefault(0);
return ret;
}
Please refer to the CppUMock documentation for details. This is just classic CppUMock boilerplate.
Last part is the Unit Test:
extern "C" {
#include "temperature.h" // UUT
};
#include "CppUTest/TestHarness.h"
#include "CppUTest/CommandLineTestRunner.h"
#include "CppUTestExt/MockSupport.h"
TEST_GROUP(Temperature)
{
void setup() {}
void teardown() {
mock().checkExpectations();
mock().clear();
}
};
TEST(Temperature, somethingA)
{
const uint8_t value = 10;
mock().ignoreOtherCalls();
mock().expectOneCall("read_from_i2c").ignoreOtherParameters()
.andReturnValue(value);
temperature_do(10, value);
}
TEST(Temperature, somethingB)
{
const uint8_t value = 10;
mock().ignoreOtherCalls();
mock().expectOneCall("read_from_i2c").ignoreOtherParameters()
.andReturnValue(value+1);
temperature_do(10, value);
}
int main(int argc, char** argv) {
return CommandLineTestRunner::RunAllTests(argc, argv);
}
This UT will actually give 100% branch coverage. Again, I cannot explain all the details. If you observe and compare test cases somethingA and somethingB, you will see what is needed to cause the UUT to once go in the path that calls somethingA() and once to go in the path that calls somethingB().
Let's take for example
mock().expectOneCall("read_from_i2c")
.ignoreOtherParameters()
.andReturnValue(value+1);
Here we are saying to CppUmock to expect a call to function read_from_i2c(), to ignore what the parameters are and, this is of fundamental importance, to return value + 1 (or anything else you fancy that is different from value). This will cause the UUT to go in the path that calls somethingB().
Happy embedded C development and happy unit testing!
I take a look into your proposed book about TDD and the mock objects. OK, so as I understand, for example in this line (mocks are already created):
mock().expectOneCall("read_from_i2c").ignoreOtherParameters()
.andReturnValue(value);
temperature_do(10, value);
the program "jumps" to the mocked "read_from_i2c" function (from i2c_mock.cpp) with parameters defined by myself and not the real one from the Unit Under Test? Or we really call our function from Unit Under Test but we manipulate this function with the parameters defined in the mock?

Unable to put strings in an array after initialisation - CCS 6 with TI compiler 5.1.8 for ARM

I'm trying to setup a multilanguage GUI for an application running on AM335x processor; developing in CCS 6.0.1 and using TI compiler 5.1.8. The concept is to get enumerated dictionary arrays and then switch current dictionary pointer to one of them, so that Im able to use enums that make sense.
enum dictionary_indexes {
name,
surname,
phone
}
const char *dictionary_en[60];
dictionary_en[name] = "Your name";
dictionary_en[surname] = "Your surname";
//and so on
Unfortunately, CCS wont compile such code. Itll only allow array initialized at the moment of declaration:
//compiles nicely:
const char * const teststring[] = {
"String1",
"String2",
};
//doesn't compile:
const char *teststring2[2];
teststring2[0]="Hello";
teststring2[1]="World";
Such code results in an error
a value of type "char [6]" cannot be used to initialize an entity of type "int [0]"
and so for every array entry.
Am I missing something here? I've used such code in the past and worked fine. Is it a compiler issue with TI, or is the issue specific for the processor? The code that is supposed to be working is based on this thread: How do I create an array of strings in C?
The teststring2 has to be a global variable, but its init can't. A little rafactor to enclose the init in an executable funciton brings relief and proper compilation, as suggested by #barakmanos.
const char *teststring2[2];
void initDict(){
teststring2[0]="Hello";
teststring2[1]="World";
}
A C file, a translation-unit, can only contain two types of elements (after pre-processing): function-definition's and declaration's. A declaration provides the type of an object and an optional initializer. What you have are statement's, which are only allowed inside a function-definition.
In other words, you need to provide the initialization at the point of declaration, or move them inside a function as an ordinary assignment. Eg:
enum dictionary_indexes {
name,
surname,
phone
}
const char *dictionary_en[60] = {
[name] = "Your name",
[surname] = "Your surname"
};
or:
void f (void)
{
dictionary_en[name] = "Your name";
dictionary_en[surname] = "Your surname";
}
Note that the { [name] = ..., } syntax in initializers was introduces in C99. If you have a compiler that conforms to an earlier standard, you need to initialize the array without designator's and in the correct order:
const char *dictionary_en[60] = {
"Your name",
"Your surname"
};

Replacing deprecated "dev_attrs" attribute with "dev_groups"

I'm trying to compile a Linux device driver (kernel module), however the module was last updated in April 2013 and of course it doesn't compile anymore on a recent (3.13) kernel, here's the error :
als_sys.c:99:2: error: unknown field ‘dev_attrs’ specified in initializer
I've searched already but all that I found were patches, there is no clear "tutorial" about updating an old module, the only thing I understood was that I need to use dev_groups instead, but it doesn't accept the same value as dev_attrs and I don't know how to adapt the existing code for that.
The code (some of it, the entire code can be found here) :
# als_sys.c
static ssize_t
illuminance_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct als_device *als = to_als_device(dev);
int illuminance;
int result;
result = als->ops->get_illuminance(als, &illuminance);
if (result)
return result;
if (!illuminance)
return sprintf(buf, "0\n");
else if (illuminance == -1)
return sprintf(buf, "-1\n");
else if (illuminance < -1)
return -ERANGE;
else
return sprintf(buf, "%d\n", illuminance);
}
# truncated - also "adjustment_show" is similar to this function so
# I didn't copy/paste it to save some space in the question
static struct device_attribute als_attrs[] = { # that's what I need to modify, but
__ATTR(illuminance, 0444, illuminance_show, NULL), # I have no clue what to
__ATTR(display_adjustment, 0444, adjustment_show, NULL), # put here instead
__ATTR_NULL,
};
# truncated
static struct class als_class = {
.name = "als",
.dev_release = als_release,
.dev_attrs = als_attrs, # line 99, that's where it fails
};
EDIT
As mentioned in the answer below, I changed the code like this :
static struct device_attribute als_attrs[] = {
__ATTR(illuminance, 0444, illuminance_show, NULL),
__ATTR(display_adjustment, 0444, adjustment_show, NULL),
__ATTR_NULL,
};
static const struct attribute_group als_attr_group = {
.attrs = als_attrs,
};
static struct class als_class = {
.name = "als",
.dev_release = als_release,
.dev_groups = als_attr_group, # line 103 - it fails here again
};
But I still get another error :
als_sys.c:103:2: error: initializer element is not constant
I've found this question which is about the same error however its answer is about a single attribute and I don't know how to adapt it for multiple ones.
Thanks for your help and have a nice day.
Indeed, dev_attrs was replaced with dev_groups in 3.13. In 3.12 they were both presented in struct. Look at 3.12 version and 3.13 version.
Anyway, there should not be a problem, because simple search for attribute_group gives you a lot of examples.
Simply put, you have to embed your dev_attrs inside dev_group:
static const struct attribute_group als_attr_group = {
.attrs = als_attrs,
};
And then use that attribute group in struct class.
There is also a handy macro ATTRIBUTE_GROUPS. See example usage https://lkml.org/lkml/2013/10/23/218.
EDIT:
Remove const declaration from attribute group like this:
static struct attribute_group als_attr_group = {
.attrs = als_attrs,
};
Because you can't initialize const struct with something that is not literal like 0xff or 'c'. See more detailes here.

Resources